diff --git a/.gitignore b/.gitignore index 71ce90db3..e2aa4de95 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ *~ .project .settings + +cmd/ethereum/ethereum +cmd/mist/mist diff --git a/.travis.yml b/.travis.yml index 0084bf9c2..2dfb7e283 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,9 @@ install: - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi - go get github.com/mattn/goveralls - go get gopkg.in/check.v1 - - DEPS=$(go list -f '{{.Imports}}' ./... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$DEPS" ]; then go get -d -v $DEPS; fi + - go get github.com/tools/godep before_script: + - godep restore - gofmt -l -w . - goimports -l -w . - golint . diff --git a/Dockerfile b/Dockerfile index 6e29a638d..907b96c21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,26 @@ -FROM ubuntu:14.04 +FROM ubuntu:14.04.1 ## Environment setup ENV HOME /root ENV GOPATH /root/go -ENV PATH /golang/bin:/root/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games -ENV PKG_CONFIG_PATH /opt/qt54/lib/pkgconfig +ENV PATH /root/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games RUN mkdir -p /root/go ENV DEBIAN_FRONTEND noninteractive ## Install base dependencies RUN apt-get update && apt-get upgrade -y -RUN apt-get install -y git mercurial build-essential software-properties-common pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev mesa-common-dev libglu1-mesa-dev +RUN apt-get install -y git mercurial build-essential software-properties-common wget pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev -## Install Qt5.4 dependencies from PPA -RUN add-apt-repository ppa:beineri/opt-qt54-trusty -y -RUN apt-get update -y -RUN apt-get install -y qt54quickcontrols qt54webengine +## Install Qt5.4 +# RUN add-apt-repository ppa:beineri/opt-qt54-trusty -y +# RUN apt-get update -y +# RUN apt-get install -y qt54quickcontrols qt54webengine mesa-common-dev libglu1-mesa-dev +# ENV PKG_CONFIG_PATH /opt/qt54/lib/pkgconfig -## Build and install latest Go -RUN git clone https://go.googlesource.com/go golang -RUN cd golang && git checkout go1.4.1 -RUN cd golang/src && ./make.bash && go version +# Install Golang +RUN wget https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz +RUN tar -C /usr/local -xzf go*.tar.gz && go version # this is a workaround, to make sure that docker's cache is invalidated whenever the git repo changes ADD https://api.github.com/repos/ethereum/go-ethereum/git/refs/heads/develop file_does_not_exist diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json new file mode 100644 index 000000000..1ecdce06f --- /dev/null +++ b/Godeps/Godeps.json @@ -0,0 +1,130 @@ +{ + "ImportPath": "github.com/ethereum/go-ethereum", + "GoVersion": "go1.4", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "bitbucket.org/kardianos/osext", + "Comment": "null-13", + "Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e" + }, + { + "ImportPath": "code.google.com/p/go-uuid/uuid", + "Comment": "null-12", + "Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9" + }, + { + "ImportPath": "code.google.com/p/go.crypto/pbkdf2", + "Comment": "null-236", + "Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e" + }, + { + "ImportPath": "code.google.com/p/go.crypto/ripemd160", + "Comment": "null-236", + "Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e" + }, + { + "ImportPath": "code.google.com/p/go.crypto/scrypt", + "Comment": "null-236", + "Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e" + }, + { + "ImportPath": "code.google.com/p/go.net/websocket", + "Comment": "null-173", + "Rev": "4231557d7c726df4cf9a4e8cdd8a417c8c200bdb" + }, + { + "ImportPath": "code.google.com/p/snappy-go/snappy", + "Comment": "null-15", + "Rev": "12e4b4183793ac4b061921e7980845e750679fd0" + }, + { + "ImportPath": "github.com/ethereum/serpent-go", + "Rev": "5767a0dbd759d313df3f404dadb7f98d7ab51443" + }, + { + "ImportPath": "github.com/fjl/goupnp", + "Rev": "fa95df6feb61e136b499d01711fcd410ccaf20c1" + }, + { + "ImportPath": "github.com/howeyc/fsnotify", + "Comment": "v0.9.0-11-g6b1ef89", + "Rev": "6b1ef893dc11e0447abda6da20a5203481878dda" + }, + { + "ImportPath": "github.com/jackpal/go-nat-pmp", + "Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" + }, + { + "ImportPath": "github.com/obscuren/ecies", + "Rev": "d899334bba7bf4a157cab19d8ad836dcb1de0c34" + }, + { + "ImportPath": "github.com/obscuren/otto", + "Rev": "cf13cc4228c5e5ce0fe27a7aea90bc10091c4f19" + }, + { + "ImportPath": "github.com/obscuren/qml", + "Rev": "807b51d4104231784fa5e336ccd26d61759a3cb2" + }, + { + "ImportPath": "github.com/rakyll/globalconf", + "Rev": "415abc325023f1a00cd2d9fa512e0e71745791a2" + }, + { + "ImportPath": "github.com/rakyll/goini", + "Rev": "907cca0f578a5316fb864ec6992dc3d9730ec58c" + }, + { + "ImportPath": "github.com/robertkrimen/otto/ast", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/robertkrimen/otto/dbg", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/robertkrimen/otto/file", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/robertkrimen/otto/parser", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/robertkrimen/otto/registry", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/robertkrimen/otto/token", + "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" + }, + { + "ImportPath": "github.com/syndtr/goleveldb/leveldb", + "Rev": "832fa7ed4d28545eab80f19e1831fc004305cade" + }, + { + "ImportPath": "golang.org/x/crypto/pbkdf2", + "Rev": "4ed45ec682102c643324fae5dff8dab085b6c300" + }, + { + "ImportPath": "gopkg.in/check.v1", + "Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673" + }, + { + "ImportPath": "gopkg.in/fatih/set.v0", + "Comment": "v0.1.0-3-g27c4092", + "Rev": "27c40922c40b43fe04554d8223a402af3ea333f3" + }, + { + "ImportPath": "gopkg.in/qml.v1/cdata", + "Rev": "1116cb9cd8dee23f8d444ded354eb53122739f99" + }, + { + "ImportPath": "gopkg.in/qml.v1/gl/glbase", + "Rev": "1116cb9cd8dee23f8d444ded354eb53122739f99" + } + ] +} diff --git a/Godeps/Readme b/Godeps/Readme new file mode 100644 index 000000000..4cdaa53d5 --- /dev/null +++ b/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore new file mode 100644 index 000000000..f037d684e --- /dev/null +++ b/Godeps/_workspace/.gitignore @@ -0,0 +1,2 @@ +/pkg +/bin diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE new file mode 100644 index 000000000..18527a28f --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 Daniel Theophanes + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go new file mode 100644 index 000000000..37efbb221 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go @@ -0,0 +1,32 @@ +// 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. + +// Extensions to the standard "os" package. +package osext + +import "path/filepath" + +// Executable returns an absolute path that can be used to +// re-invoke the current program. +// It may not be valid after the current program exits. +func Executable() (string, error) { + p, err := executable() + return filepath.Clean(p), err +} + +// Returns same path as Executable, returns just the folder +// path. Excludes the executable name. +func ExecutableFolder() (string, error) { + p, err := Executable() + if err != nil { + return "", err + } + folder, _ := filepath.Split(p) + return folder, nil +} + +// Depricated. Same as Executable(). +func GetExePath() (exePath string, err error) { + return Executable() +} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go new file mode 100644 index 000000000..4468a73a7 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go @@ -0,0 +1,20 @@ +// 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 osext + +import ( + "syscall" + "os" + "strconv" +) + +func executable() (string, error) { + f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") + if err != nil { + return "", err + } + defer f.Close() + return syscall.Fd2path(int(f.Fd())) +} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go new file mode 100644 index 000000000..546fec915 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go @@ -0,0 +1,25 @@ +// 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 linux netbsd openbsd + +package osext + +import ( + "errors" + "os" + "runtime" +) + +func executable() (string, error) { + switch runtime.GOOS { + case "linux": + return os.Readlink("/proc/self/exe") + case "netbsd": + return os.Readlink("/proc/curproc/exe") + case "openbsd": + return os.Readlink("/proc/curproc/file") + } + return "", errors.New("ExecPath not implemented for " + runtime.GOOS) +} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go new file mode 100644 index 000000000..b66cac878 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go @@ -0,0 +1,79 @@ +// 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 darwin freebsd + +package osext + +import ( + "os" + "path/filepath" + "runtime" + "syscall" + "unsafe" +) + +var initCwd, initCwdErr = os.Getwd() + +func executable() (string, error) { + var mib [4]int32 + switch runtime.GOOS { + case "freebsd": + mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} + case "darwin": + mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} + } + + n := uintptr(0) + // Get length. + _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) + if errNum != 0 { + return "", errNum + } + if n == 0 { // This shouldn't happen. + return "", nil + } + buf := make([]byte, n) + _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) + if errNum != 0 { + return "", errNum + } + if n == 0 { // This shouldn't happen. + return "", nil + } + for i, v := range buf { + if v == 0 { + buf = buf[:i] + break + } + } + var err error + execPath := string(buf) + // execPath will not be empty due to above checks. + // Try to get the absolute path if the execPath is not rooted. + if execPath[0] != '/' { + execPath, err = getAbs(execPath) + if err != nil { + return execPath, err + } + } + // For darwin KERN_PROCARGS may return the path to a symlink rather than the + // actual executable. + if runtime.GOOS == "darwin" { + if execPath, err = filepath.EvalSymlinks(execPath); err != nil { + return execPath, err + } + } + return execPath, nil +} + +func getAbs(execPath string) (string, error) { + if initCwdErr != nil { + return execPath, initCwdErr + } + // The execPath may begin with a "../" or a "./" so clean it first. + // Join the two paths, trailing and starting slashes undetermined, so use + // the generic Join function. + return filepath.Join(initCwd, filepath.Clean(execPath)), nil +} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go new file mode 100644 index 000000000..dc661dbc2 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go @@ -0,0 +1,79 @@ +// 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 darwin linux freebsd netbsd windows + +package osext + +import ( + "fmt" + "os" + oexec "os/exec" + "path/filepath" + "runtime" + "testing" +) + +const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH" + +func TestExecPath(t *testing.T) { + ep, err := Executable() + if err != nil { + t.Fatalf("ExecPath failed: %v", err) + } + // we want fn to be of the form "dir/prog" + dir := filepath.Dir(filepath.Dir(ep)) + fn, err := filepath.Rel(dir, ep) + if err != nil { + t.Fatalf("filepath.Rel: %v", err) + } + cmd := &oexec.Cmd{} + // make child start with a relative program path + cmd.Dir = dir + cmd.Path = fn + // forge argv[0] for child, so that we can verify we could correctly + // get real path of the executable without influenced by argv[0]. + cmd.Args = []string{"-", "-test.run=XXXX"} + cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)} + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("exec(self) failed: %v", err) + } + outs := string(out) + if !filepath.IsAbs(outs) { + t.Fatalf("Child returned %q, want an absolute path", out) + } + if !sameFile(outs, ep) { + t.Fatalf("Child returned %q, not the same file as %q", out, ep) + } +} + +func sameFile(fn1, fn2 string) bool { + fi1, err := os.Stat(fn1) + if err != nil { + return false + } + fi2, err := os.Stat(fn2) + if err != nil { + return false + } + return os.SameFile(fi1, fi2) +} + +func init() { + if e := os.Getenv(execPath_EnvVar); e != "" { + // first chdir to another path + dir := "/" + if runtime.GOOS == "windows" { + dir = filepath.VolumeName(".") + } + os.Chdir(dir) + if ep, err := Executable(); err != nil { + fmt.Fprint(os.Stderr, "ERROR: ", err) + } else { + fmt.Fprint(os.Stderr, ep) + } + os.Exit(0) + } +} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go new file mode 100644 index 000000000..72d282cf8 --- /dev/null +++ b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go @@ -0,0 +1,34 @@ +// 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 osext + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + kernel = syscall.MustLoadDLL("kernel32.dll") + getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW") +) + +// GetModuleFileName() with hModule = NULL +func executable() (exePath string, err error) { + return getModuleFileName() +} + +func getModuleFileName() (string, error) { + var n uint32 + b := make([]uint16, syscall.MAX_PATH) + size := uint32(len(b)) + + r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size)) + n = uint32(r0) + if n == 0 { + return "", e1 + } + return string(utf16.Decode(b[0:n])), nil +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE new file mode 100644 index 000000000..ab6b011a1 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go new file mode 100644 index 000000000..50a0f2d09 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go @@ -0,0 +1,84 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) UUID { + uuid := NewUUID() + if uuid != nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCEPerson(Person, uint32(os.Getuid())) +func NewDCEPerson() UUID { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCEGroup(Group, uint32(os.Getgid())) +func NewDCEGroup() UUID { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID or false. +func (uuid UUID) Domain() (Domain, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return Domain(uuid[9]), true +} + +// Id returns the id for a Version 2 UUID or false. +func (uuid UUID) Id() (uint32, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return binary.BigEndian.Uint32(uuid[0:4]), true +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go new file mode 100644 index 000000000..d8bd013e6 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go @@ -0,0 +1,8 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uuid package generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. +package uuid diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go new file mode 100644 index 000000000..cdd4192fd --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known Name Space IDs and UUIDs +var ( + NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NIL = Parse("00000000-0000-0000-0000-000000000000") +) + +// NewHash returns a new UUID dervied from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space) + h.Write([]byte(data)) + s := h.Sum(nil) + uuid := make([]byte, 16) + copy(uuid, s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go new file mode 100644 index 000000000..dd0a8ac18 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go @@ -0,0 +1,101 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "net" + +var ( + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID []byte // hardware for version 1 UUIDs +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + if setNodeID(ifs.HardwareAddr) { + ifname = ifs.Name + return true + } + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + if nodeID == nil { + nodeID = make([]byte, 6) + } + randomBits(nodeID) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + if nodeID == nil { + SetNodeInterface("") + } + nid := make([]byte, 6) + copy(nid, nodeID) + return nid +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if setNodeID(id) { + ifname = "user" + return true + } + return false +} + +func setNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + if nodeID == nil { + nodeID = make([]byte, 6) + } + copy(nodeID, id) + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + node := make([]byte, 6) + copy(node, uuid[10:]) + return node +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go new file mode 100644 index 000000000..b9369c200 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go @@ -0,0 +1,132 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + mu sync.Mutex + lasttime uint64 // last time we returned + clock_seq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// adjusts the clock sequence as needed. An error is returned if the current +// time cannot be determined. +func GetTime() (Time, error) { + defer mu.Unlock() + mu.Lock() + return getTime() +} + +func getTime() (Time, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clock_seq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence a new random +// clock sequence is generated the first time a clock sequence is requested by +// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated +// for +func ClockSequence() int { + defer mu.Unlock() + mu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clock_seq == 0 { + setClockSequence(-1) + } + return int(clock_seq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer mu.Unlock() + mu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clock_seq + clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clock_seq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. It returns false if uuid is not valid. The time is only well defined +// for version 1 and 2 UUIDs. +func (uuid UUID) Time() (Time, bool) { + if len(uuid) != 16 { + return 0, false + } + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time), true +} + +// ClockSequence returns the clock sequence encoded in uuid. It returns false +// if uuid is not valid. The clock sequence is only well defined for version 1 +// and 2 UUIDs. +func (uuid UUID) ClockSequence() (int, bool) { + if len(uuid) != 16 { + return 0, false + } + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go new file mode 100644 index 000000000..de40b102c --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts the the first two hex bytes of x into a byte. +func xtob(x string) (byte, bool) { + b1 := xvalues[x[0]] + b2 := xvalues[x[1]] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go new file mode 100644 index 000000000..2920fae63 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go @@ -0,0 +1,163 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID []byte + +// A Version represents a UUIDs version. +type Version byte + +// A Variant represents a UUIDs variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// New returns a new random (version 4) UUID as a string. It is a convenience +// function for NewRandom().String(). +func New() string { + return NewRandom().String() +} + +// Parse decodes s into a UUID or returns nil. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) UUID { + if len(s) == 36+9 { + if strings.ToLower(s[:9]) != "urn:uuid:" { + return nil + } + s = s[9:] + } else if len(s) != 36 { + return nil + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return nil + } + uuid := make([]byte, 16) + for i, x := range []int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x:]); !ok { + return nil + } else { + uuid[i] = v + } + } + return uuid +} + +// Equal returns true if uuid1 and uuid2 are equal. +func Equal(uuid1, uuid2 UUID) bool { + return bytes.Equal(uuid1, uuid2) +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// Variant returns the variant encoded in uuid. It returns Invalid if +// uuid is invalid. +func (uuid UUID) Variant() Variant { + if len(uuid) != 16 { + return Invalid + } + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } + panic("unreachable") +} + +// Version returns the verison of uuid. It returns false if uuid is not +// valid. +func (uuid UUID) Version() (Version, bool) { + if len(uuid) != 16 { + return 0, false + } + return Version(uuid[6] >> 4), true +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implents io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go new file mode 100644 index 000000000..417ebeb26 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go @@ -0,0 +1,390 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + "time" +) + +type test struct { + in string + version Version + variant Variant + isuuid bool +} + +var tests = []test{ + {"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true}, + {"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true}, + {"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true}, + {"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true}, + {"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true}, + {"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true}, + {"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true}, + {"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true}, + {"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true}, + {"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true}, + {"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true}, + {"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true}, + {"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true}, + {"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true}, + + {"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true}, + {"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true}, + + {"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false}, + {"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false}, +} + +var constants = []struct { + c interface{} + name string +}{ + {Person, "Person"}, + {Group, "Group"}, + {Org, "Org"}, + {Invalid, "Invalid"}, + {RFC4122, "RFC4122"}, + {Reserved, "Reserved"}, + {Microsoft, "Microsoft"}, + {Future, "Future"}, + {Domain(17), "Domain17"}, + {Variant(42), "BadVariant42"}, +} + +func testTest(t *testing.T, in string, tt test) { + uuid := Parse(in) + if ok := (uuid != nil); ok != tt.isuuid { + t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid) + } + if uuid == nil { + return + } + + if v := uuid.Variant(); v != tt.variant { + t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant) + } + if v, _ := uuid.Version(); v != tt.version { + t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version) + } +} + +func TestUUID(t *testing.T) { + for _, tt := range tests { + testTest(t, tt.in, tt) + testTest(t, strings.ToUpper(tt.in), tt) + } +} + +func TestConstants(t *testing.T) { + for x, tt := range constants { + v, ok := tt.c.(fmt.Stringer) + if !ok { + t.Errorf("%x: %v: not a stringer", x, v) + } else if s := v.String(); s != tt.name { + v, _ := tt.c.(int) + t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name) + } + } +} + +func TestRandomUUID(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + uuid := NewRandom() + s := uuid.String() + if m[s] { + t.Errorf("NewRandom returned duplicated UUID %s\n", s) + } + m[s] = true + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func TestNew(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + s := New() + if m[s] { + t.Errorf("New returned duplicated UUID %s\n", s) + } + m[s] = true + uuid := Parse(s) + if uuid == nil { + t.Errorf("New returned %q which does not decode\n", s) + continue + } + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func clockSeq(t *testing.T, uuid UUID) int { + seq, ok := uuid.ClockSequence() + if !ok { + t.Fatalf("%s: invalid clock sequence\n", uuid) + } + return seq +} + +func TestClockSeq(t *testing.T) { + // Fake time.Now for this test to return a monotonically advancing time; restore it at end. + defer func(orig func() time.Time) { timeNow = orig }(timeNow) + monTime := time.Now() + timeNow = func() time.Time { + monTime = monTime.Add(1 * time.Second) + return monTime + } + + SetClockSequence(-1) + uuid1 := NewUUID() + uuid2 := NewUUID() + + if clockSeq(t, uuid1) != clockSeq(t, uuid2) { + t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2)) + } + + SetClockSequence(-1) + uuid2 = NewUUID() + + // Just on the very off chance we generated the same sequence + // two times we try again. + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + SetClockSequence(-1) + uuid2 = NewUUID() + } + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1)) + } + + SetClockSequence(0x1234) + uuid1 = NewUUID() + if seq := clockSeq(t, uuid1); seq != 0x1234 { + t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq) + } +} + +func TestCoding(t *testing.T) { + text := "7d444840-9dc0-11d1-b245-5ffdce74fad2" + urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2" + data := UUID{ + 0x7d, 0x44, 0x48, 0x40, + 0x9d, 0xc0, + 0x11, 0xd1, + 0xb2, 0x45, + 0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2, + } + if v := data.String(); v != text { + t.Errorf("%x: encoded to %s, expected %s\n", data, v, text) + } + if v := data.URN(); v != urn { + t.Errorf("%x: urn is %s, expected %s\n", data, v, urn) + } + + uuid := Parse(text) + if !Equal(uuid, data) { + t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data) + } +} + +func TestVersion1(t *testing.T) { + uuid1 := NewUUID() + uuid2 := NewUUID() + + if Equal(uuid1, uuid2) { + t.Errorf("%s:duplicate uuid\n", uuid1) + } + if v, _ := uuid1.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid1, v) + } + if v, _ := uuid2.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid2, v) + } + n1 := uuid1.NodeID() + n2 := uuid2.NodeID() + if !bytes.Equal(n1, n2) { + t.Errorf("Different nodes %x != %x\n", n1, n2) + } + t1, ok := uuid1.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid1) + } + t2, ok := uuid2.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid2) + } + q1, ok := uuid1.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence\n", uuid1) + } + q2, ok := uuid2.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence", uuid2) + } + + switch { + case t1 == t2 && q1 == q2: + t.Errorf("time stopped\n") + case t1 > t2 && q1 == q2: + t.Errorf("time reversed\n") + case t1 < t2 && q1 != q2: + t.Errorf("clock sequence chaned unexpectedly\n") + } +} + +func TestNodeAndTime(t *testing.T) { + // Time is February 5, 1998 12:30:23.136364800 AM GMT + + uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2} + + ts, ok := uuid.Time() + if ok { + c := time.Unix(ts.UnixTime()) + want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) + if !c.Equal(want) { + t.Errorf("Got time %v, want %v", c, want) + } + } else { + t.Errorf("%s: bad time\n", uuid) + } + if !bytes.Equal(node, uuid.NodeID()) { + t.Errorf("Expected node %v got %v\n", node, uuid.NodeID()) + } +} + +func TestMD5(t *testing.T) { + uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String() + want := "6fa459ea-ee8a-3ca4-894e-db77e160355e" + if uuid != want { + t.Errorf("MD5: got %q expected %q\n", uuid, want) + } +} + +func TestSHA1(t *testing.T) { + uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String() + want := "886313e1-3b8a-5372-9b90-0c9aee199e5d" + if uuid != want { + t.Errorf("SHA1: got %q expected %q\n", uuid, want) + } +} + +func TestNodeID(t *testing.T) { + nid := []byte{1, 2, 3, 4, 5, 6} + SetNodeInterface("") + s := NodeInterface() + if s == "" || s == "user" { + t.Errorf("NodeInterface %q after SetInteface\n", s) + } + node1 := NodeID() + if node1 == nil { + t.Errorf("NodeID nil after SetNodeInterface\n", s) + } + SetNodeID(nid) + s = NodeInterface() + if s != "user" { + t.Errorf("Expected NodeInterface %q got %q\n", "user", s) + } + node2 := NodeID() + if node2 == nil { + t.Errorf("NodeID nil after SetNodeID\n", s) + } + if bytes.Equal(node1, node2) { + t.Errorf("NodeID not changed after SetNodeID\n", s) + } else if !bytes.Equal(nid, node2) { + t.Errorf("NodeID is %x, expected %x\n", node2, nid) + } +} + +func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) { + if uuid == nil { + t.Errorf("%s failed\n", name) + return + } + if v, _ := uuid.Version(); v != 2 { + t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v) + return + } + if v, ok := uuid.Domain(); !ok || v != domain { + if !ok { + t.Errorf("%s: %d: Domain failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v) + } + } + if v, ok := uuid.Id(); !ok || v != id { + if !ok { + t.Errorf("%s: %d: Id failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v) + } + } +} + +func TestDCE(t *testing.T) { + testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678) + testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid())) + testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid())) +} + +type badRand struct{} + +func (r badRand) Read(buf []byte) (int, error) { + for i, _ := range buf { + buf[i] = byte(i) + } + return len(buf), nil +} + +func TestBadRand(t *testing.T) { + SetRand(badRand{}) + uuid1 := New() + uuid2 := New() + if uuid1 != uuid2 { + t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2) + } + SetRand(nil) + uuid1 = New() + uuid2 = New() + if uuid1 == uuid2 { + t.Errorf("unexecpted duplicates, got %q\n", uuid1) + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go new file mode 100644 index 000000000..63580044b --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil. +func NewUUID() UUID { + if nodeID == nil { + SetNodeInterface("") + } + + now, err := GetTime() + if err != nil { + return nil + } + + uuid := make([]byte, 16) + + time_low := uint32(now & 0xffffffff) + time_mid := uint16((now >> 32) & 0xffff) + time_hi := uint16((now >> 48) & 0x0fff) + time_hi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], time_low) + binary.BigEndian.PutUint16(uuid[4:], time_mid) + binary.BigEndian.PutUint16(uuid[6:], time_hi) + binary.BigEndian.PutUint16(uuid[8:], clock_seq) + copy(uuid[10:], nodeID) + + return uuid +} diff --git a/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go new file mode 100644 index 000000000..b3d4a368d --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go @@ -0,0 +1,25 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +// Random returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() UUID { + uuid := make([]byte, 16) + randomBits([]byte(uuid)) + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2.go new file mode 100644 index 000000000..c02b4d5a7 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2.go @@ -0,0 +1,77 @@ +// 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC +2898 / PKCS #5 v2.0. + +A key derivation function is useful when encrypting data based on a password +or any other not-fully-random data. It uses a pseudorandom function to derive +a secure encryption key based on the password. + +While v2.0 of the standard defines only one pseudorandom function to use, +HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved +Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To +choose, you can pass the `New` functions from the different SHA packages to +pbkdf2.Key. +*/ +package pbkdf2 + +import ( + "crypto/hmac" + "hash" +) + +// Key derives a key from the password, salt and iteration count, returning a +// []byte of length keylen that can be used as cryptographic key. The key is +// derived based on the method described as PBKDF2 with the HMAC variant using +// the supplied hash function. +// +// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you +// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by +// doing: +// +// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) +// +// Remember to get a good random salt. At least 8 bytes is recommended by the +// RFC. +// +// Using a higher iteration count will increase the cost of an exhaustive +// search but will also make derivation proportionally slower. +func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { + prf := hmac.New(h, password) + hashLen := prf.Size() + numBlocks := (keyLen + hashLen - 1) / hashLen + + var buf [4]byte + dk := make([]byte, 0, numBlocks*hashLen) + U := make([]byte, hashLen) + for block := 1; block <= numBlocks; block++ { + // N.B.: || means concatenation, ^ means XOR + // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter + // U_1 = PRF(password, salt || uint(i)) + prf.Reset() + prf.Write(salt) + buf[0] = byte(block >> 24) + buf[1] = byte(block >> 16) + buf[2] = byte(block >> 8) + buf[3] = byte(block) + prf.Write(buf[:4]) + dk = prf.Sum(dk) + T := dk[len(dk)-hashLen:] + copy(U, T) + + // U_n = PRF(password, U_(n-1)) + for n := 2; n <= iter; n++ { + prf.Reset() + prf.Write(U) + U = U[:0] + U = prf.Sum(U) + for x := range U { + T[x] ^= U[x] + } + } + } + return dk[:keyLen] +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2_test.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2_test.go new file mode 100644 index 000000000..137924061 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2_test.go @@ -0,0 +1,157 @@ +// 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 pbkdf2 + +import ( + "bytes" + "crypto/sha1" + "crypto/sha256" + "hash" + "testing" +) + +type testVector struct { + password string + salt string + iter int + output []byte +} + +// Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070 +var sha1TestVectors = []testVector{ + { + "password", + "salt", + 1, + []byte{ + 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6, + }, + }, + { + "password", + "salt", + 2, + []byte{ + 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57, + }, + }, + { + "password", + "salt", + 4096, + []byte{ + 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1, + }, + }, + // // This one takes too long + // { + // "password", + // "salt", + // 16777216, + // []byte{ + // 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, + // 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, + // 0x26, 0x34, 0xe9, 0x84, + // }, + // }, + { + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + []byte{ + 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38, + }, + }, + { + "pass\000word", + "sa\000lt", + 4096, + []byte{ + 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3, + }, + }, +} + +// Test vectors from +// http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors +var sha256TestVectors = []testVector{ + { + "password", + "salt", + 1, + []byte{ + 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, + 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, + 0xa8, 0x65, 0x48, 0xc9, + }, + }, + { + "password", + "salt", + 2, + []byte{ + 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, + 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, + 0x2a, 0x30, 0x3f, 0x8e, + }, + }, + { + "password", + "salt", + 4096, + []byte{ + 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, + 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, + 0x96, 0x28, 0x93, 0xa0, + }, + }, + { + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + []byte{ + 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, + 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, + 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, + 0x1c, + }, + }, + { + "pass\000word", + "sa\000lt", + 4096, + []byte{ + 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, + 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, + }, + }, +} + +func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) { + for i, v := range vectors { + o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h) + if !bytes.Equal(o, v.output) { + t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o) + } + } +} + +func TestWithHMACSHA1(t *testing.T) { + testHash(t, sha1.New, "SHA1", sha1TestVectors) +} + +func TestWithHMACSHA256(t *testing.T) { + testHash(t, sha256.New, "SHA256", sha256TestVectors) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160.go new file mode 100644 index 000000000..da690f0b9 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160.go @@ -0,0 +1,120 @@ +// 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 ripemd160 implements the RIPEMD-160 hash algorithm. +package ripemd160 + +// RIPEMD-160 is designed by by Hans Dobbertin, Antoon Bosselaers, and Bart +// Preneel with specifications available at: +// http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf. + +import ( + "crypto" + "hash" +) + +func init() { + crypto.RegisterHash(crypto.RIPEMD160, New) +} + +// The size of the checksum in bytes. +const Size = 20 + +// The block size of the hash algorithm in bytes. +const BlockSize = 64 + +const ( + _s0 = 0x67452301 + _s1 = 0xefcdab89 + _s2 = 0x98badcfe + _s3 = 0x10325476 + _s4 = 0xc3d2e1f0 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + s [5]uint32 // running context + x [BlockSize]byte // temporary buffer + nx int // index into x + tc uint64 // total count of bytes processed +} + +func (d *digest) Reset() { + d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4 + d.nx = 0 + d.tc = 0 +} + +// New returns a new hash.Hash computing the checksum. +func New() hash.Hash { + result := new(digest) + result.Reset() + return result +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.tc += uint64(nn) + if d.nx > 0 { + n := len(p) + if n > BlockSize-d.nx { + n = BlockSize - d.nx + } + for i := 0; i < n; i++ { + d.x[d.nx+i] = p[i] + } + d.nx += n + if d.nx == BlockSize { + _Block(d, d.x[0:]) + d.nx = 0 + } + p = p[n:] + } + n := _Block(d, p) + p = p[n:] + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +func (d0 *digest) Sum(in []byte) []byte { + // Make a copy of d0 so that caller can keep writing and summing. + d := *d0 + + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + tc := d.tc + var tmp [64]byte + tmp[0] = 0x80 + if tc%64 < 56 { + d.Write(tmp[0 : 56-tc%64]) + } else { + d.Write(tmp[0 : 64+56-tc%64]) + } + + // Length in bits. + tc <<= 3 + for i := uint(0); i < 8; i++ { + tmp[i] = byte(tc >> (8 * i)) + } + d.Write(tmp[0:8]) + + if d.nx != 0 { + panic("d.nx != 0") + } + + var digest [Size]byte + for i, s := range d.s { + digest[i*4] = byte(s) + digest[i*4+1] = byte(s >> 8) + digest[i*4+2] = byte(s >> 16) + digest[i*4+3] = byte(s >> 24) + } + + return append(in, digest[:]...) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160_test.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160_test.go new file mode 100644 index 000000000..5df1b2593 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160_test.go @@ -0,0 +1,64 @@ +// 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 ripemd160 + +// Test vectors are from: +// http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + +import ( + "fmt" + "io" + "testing" +) + +type mdTest struct { + out string + in string +} + +var vectors = [...]mdTest{ + {"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, + {"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, + {"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, + {"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, + {"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, + {"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + {"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + {"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, +} + +func TestVectors(t *testing.T) { + for i := 0; i < len(vectors); i++ { + tv := vectors[i] + md := New() + for j := 0; j < 3; j++ { + if j < 2 { + io.WriteString(md, tv.in) + } else { + io.WriteString(md, tv.in[0:len(tv.in)/2]) + md.Sum(nil) + io.WriteString(md, tv.in[len(tv.in)/2:]) + } + s := fmt.Sprintf("%x", md.Sum(nil)) + if s != tv.out { + t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out) + } + md.Reset() + } + } +} + +func TestMillionA(t *testing.T) { + md := New() + for i := 0; i < 100000; i++ { + io.WriteString(md, "aaaaaaaaaa") + } + out := "52783243c1697bdbe16d37f97f68f08325dc1528" + s := fmt.Sprintf("%x", md.Sum(nil)) + if s != out { + t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out) + } + md.Reset() +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160block.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160block.go new file mode 100644 index 000000000..7bc8e6c48 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160/ripemd160block.go @@ -0,0 +1,161 @@ +// 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. + +// RIPEMD-160 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package ripemd160 + +// work buffer indices and roll amounts for one line +var _n = [80]uint{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, +} + +var _r = [80]uint{ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, +} + +// same for the other parallel one +var n_ = [80]uint{ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, +} + +var r_ = [80]uint{ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, +} + +func _Block(md *digest, p []byte) int { + n := 0 + var x [16]uint32 + var alpha, beta uint32 + for len(p) >= BlockSize { + a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4] + aa, bb, cc, dd, ee := a, b, c, d, e + j := 0 + for i := 0; i < 16; i++ { + x[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 + j += 4 + } + + // round 1 + i := 0 + for i < 16 { + alpha = a + (b ^ c ^ d) + x[_n[i]] + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 2 + for i < 32 { + alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999 + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 3 + for i < 48 { + alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1 + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 4 + for i < 64 { + alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 5 + for i < 80 { + alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb ^ cc ^ dd) + x[n_[i]] + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // combine results + dd += c + md.s[1] + md.s[1] = md.s[2] + d + ee + md.s[2] = md.s[3] + e + aa + md.s[3] = md.s[4] + a + bb + md.s[4] = md.s[0] + b + cc + md.s[0] = dd + + p = p[BlockSize:] + n += BlockSize + } + return n +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt.go new file mode 100644 index 000000000..30737b0a6 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt.go @@ -0,0 +1,243 @@ +// 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 scrypt implements the scrypt key derivation function as defined in +// Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard +// Functions" (http://www.tarsnap.com/scrypt/scrypt.pdf). +package scrypt + +import ( + "crypto/sha256" + "errors" + + "golang.org/x/crypto/pbkdf2" +) + +const maxInt = int(^uint(0) >> 1) + +// blockCopy copies n numbers from src into dst. +func blockCopy(dst, src []uint32, n int) { + copy(dst, src[:n]) +} + +// blockXOR XORs numbers from dst with n numbers from src. +func blockXOR(dst, src []uint32, n int) { + for i, v := range src[:n] { + dst[i] ^= v + } +} + +// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, +// and puts the result into both both tmp and out. +func salsaXOR(tmp *[16]uint32, in, out []uint32) { + w0 := tmp[0] ^ in[0] + w1 := tmp[1] ^ in[1] + w2 := tmp[2] ^ in[2] + w3 := tmp[3] ^ in[3] + w4 := tmp[4] ^ in[4] + w5 := tmp[5] ^ in[5] + w6 := tmp[6] ^ in[6] + w7 := tmp[7] ^ in[7] + w8 := tmp[8] ^ in[8] + w9 := tmp[9] ^ in[9] + w10 := tmp[10] ^ in[10] + w11 := tmp[11] ^ in[11] + w12 := tmp[12] ^ in[12] + w13 := tmp[13] ^ in[13] + w14 := tmp[14] ^ in[14] + w15 := tmp[15] ^ in[15] + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8 + x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15 + + for i := 0; i < 8; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += w0 + x1 += w1 + x2 += w2 + x3 += w3 + x4 += w4 + x5 += w5 + x6 += w6 + x7 += w7 + x8 += w8 + x9 += w9 + x10 += w10 + x11 += w11 + x12 += w12 + x13 += w13 + x14 += w14 + x15 += w15 + + out[0], tmp[0] = x0, x0 + out[1], tmp[1] = x1, x1 + out[2], tmp[2] = x2, x2 + out[3], tmp[3] = x3, x3 + out[4], tmp[4] = x4, x4 + out[5], tmp[5] = x5, x5 + out[6], tmp[6] = x6, x6 + out[7], tmp[7] = x7, x7 + out[8], tmp[8] = x8, x8 + out[9], tmp[9] = x9, x9 + out[10], tmp[10] = x10, x10 + out[11], tmp[11] = x11, x11 + out[12], tmp[12] = x12, x12 + out[13], tmp[13] = x13, x13 + out[14], tmp[14] = x14, x14 + out[15], tmp[15] = x15, x15 +} + +func blockMix(tmp *[16]uint32, in, out []uint32, r int) { + blockCopy(tmp[:], in[(2*r-1)*16:], 16) + for i := 0; i < 2*r; i += 2 { + salsaXOR(tmp, in[i*16:], out[i*8:]) + salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:]) + } +} + +func integer(b []uint32, r int) uint64 { + j := (2*r - 1) * 16 + return uint64(b[j]) | uint64(b[j+1])<<32 +} + +func smix(b []byte, r, N int, v, xy []uint32) { + var tmp [16]uint32 + x := xy + y := xy[32*r:] + + j := 0 + for i := 0; i < 32*r; i++ { + x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 + j += 4 + } + for i := 0; i < N; i += 2 { + blockCopy(v[i*(32*r):], x, 32*r) + blockMix(&tmp, x, y, r) + + blockCopy(v[(i+1)*(32*r):], y, 32*r) + blockMix(&tmp, y, x, r) + } + for i := 0; i < N; i += 2 { + j := int(integer(x, r) & uint64(N-1)) + blockXOR(x, v[j*(32*r):], 32*r) + blockMix(&tmp, x, y, r) + + j = int(integer(y, r) & uint64(N-1)) + blockXOR(y, v[j*(32*r):], 32*r) + blockMix(&tmp, y, x, r) + } + j = 0 + for _, v := range x[:32*r] { + b[j+0] = byte(v >> 0) + b[j+1] = byte(v >> 8) + b[j+2] = byte(v >> 16) + b[j+3] = byte(v >> 24) + j += 4 + } +} + +// Key derives a key from the password, salt, and cost parameters, returning +// a byte slice of length keyLen that can be used as cryptographic key. +// +// N is a CPU/memory cost parameter, which must be a power of two greater than 1. +// r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the +// limits, the function returns a nil byte slice and an error. +// +// For example, you can get a derived key for e.g. AES-256 (which needs a +// 32-byte key) by doing: +// +// dk := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) +// +// The recommended parameters for interactive logins as of 2009 are N=16384, +// r=8, p=1. They should be increased as memory latency and CPU parallelism +// increases. Remember to get a good random salt. +func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { + if N <= 1 || N&(N-1) != 0 { + return nil, errors.New("scrypt: N must be > 1 and a power of 2") + } + if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { + return nil, errors.New("scrypt: parameters are too large") + } + + xy := make([]uint32, 64*r) + v := make([]uint32, 32*N*r) + b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) + + for i := 0; i < p; i++ { + smix(b[i*128*r:], r, N, v, xy) + } + + return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt_test.go b/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt_test.go new file mode 100644 index 000000000..e096c3a31 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.crypto/scrypt/scrypt_test.go @@ -0,0 +1,160 @@ +// 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 scrypt + +import ( + "bytes" + "testing" +) + +type testVector struct { + password string + salt string + N, r, p int + output []byte +} + +var good = []testVector{ + { + "password", + "salt", + 2, 10, 10, + []byte{ + 0x48, 0x2c, 0x85, 0x8e, 0x22, 0x90, 0x55, 0xe6, 0x2f, + 0x41, 0xe0, 0xec, 0x81, 0x9a, 0x5e, 0xe1, 0x8b, 0xdb, + 0x87, 0x25, 0x1a, 0x53, 0x4f, 0x75, 0xac, 0xd9, 0x5a, + 0xc5, 0xe5, 0xa, 0xa1, 0x5f, + }, + }, + { + "password", + "salt", + 16, 100, 100, + []byte{ + 0x88, 0xbd, 0x5e, 0xdb, 0x52, 0xd1, 0xdd, 0x0, 0x18, + 0x87, 0x72, 0xad, 0x36, 0x17, 0x12, 0x90, 0x22, 0x4e, + 0x74, 0x82, 0x95, 0x25, 0xb1, 0x8d, 0x73, 0x23, 0xa5, + 0x7f, 0x91, 0x96, 0x3c, 0x37, + }, + }, + { + "this is a long \000 password", + "and this is a long \000 salt", + 16384, 8, 1, + []byte{ + 0xc3, 0xf1, 0x82, 0xee, 0x2d, 0xec, 0x84, 0x6e, 0x70, + 0xa6, 0x94, 0x2f, 0xb5, 0x29, 0x98, 0x5a, 0x3a, 0x09, + 0x76, 0x5e, 0xf0, 0x4c, 0x61, 0x29, 0x23, 0xb1, 0x7f, + 0x18, 0x55, 0x5a, 0x37, 0x07, 0x6d, 0xeb, 0x2b, 0x98, + 0x30, 0xd6, 0x9d, 0xe5, 0x49, 0x26, 0x51, 0xe4, 0x50, + 0x6a, 0xe5, 0x77, 0x6d, 0x96, 0xd4, 0x0f, 0x67, 0xaa, + 0xee, 0x37, 0xe1, 0x77, 0x7b, 0x8a, 0xd5, 0xc3, 0x11, + 0x14, 0x32, 0xbb, 0x3b, 0x6f, 0x7e, 0x12, 0x64, 0x40, + 0x18, 0x79, 0xe6, 0x41, 0xae, + }, + }, + { + "p", + "s", + 2, 1, 1, + []byte{ + 0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11, 0x98, + 0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52, + }, + }, + + { + "", + "", + 16, 1, 1, + []byte{ + 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, + 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97, 0xf1, 0x6b, + 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, + 0x3f, 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d, + 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 0x75, 0x3a, 0x0f, + 0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, + 0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, + 0x06, + }, + }, + { + "password", + "NaCl", + 1024, 8, 16, + []byte{ + 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, + 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe, 0x7c, 0x6a, + 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, + 0x63, 0x4b, 0x37, 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9, + 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 0x09, 0x27, 0x9d, + 0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, + 0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, + 0x40, + }, + }, + { + "pleaseletmein", "SodiumChloride", + 16384, 8, 1, + []byte{ + 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, + 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb, 0xfd, 0xa8, + 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, + 0xf6, 0x54, 0x5d, 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55, + 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 0x97, 0x05, 0x24, + 0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, + 0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, + 0x87, + }, + }, + /* + // Disabled: needs 1 GiB RAM and takes too long for a simple test. + { + "pleaseletmein", "SodiumChloride", + 1048576, 8, 1, + []byte{ + 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, + 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81, 0xec, 0x56, + 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, + 0x98, 0x20, 0xad, 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f, + 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 0x6d, 0x92, 0x7c, + 0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, + 0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, + 0xa4, + }, + }, + */ +} + +var bad = []testVector{ + {"p", "s", 0, 1, 1, nil}, // N == 0 + {"p", "s", 1, 1, 1, nil}, // N == 1 + {"p", "s", 7, 8, 1, nil}, // N is not power of 2 + {"p", "s", 16, maxInt / 2, maxInt / 2, nil}, // p * r too large +} + +func TestKey(t *testing.T) { + for i, v := range good { + k, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, len(v.output)) + if err != nil { + t.Errorf("%d: got unexpected error: %s", i, err) + } + if !bytes.Equal(k, v.output) { + t.Errorf("%d: expected %x, got %x", i, v.output, k) + } + } + for i, v := range bad { + _, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, 32) + if err == nil { + t.Errorf("%d: expected error, got nil", i) + } + } +} + +func BenchmarkKey(b *testing.B) { + for i := 0; i < b.N; i++ { + Key([]byte("password"), []byte("salt"), 16384, 8, 1, 64) + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/client.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/client.go new file mode 100644 index 000000000..a861bb92c --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/client.go @@ -0,0 +1,98 @@ +// 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 websocket + +import ( + "bufio" + "crypto/tls" + "io" + "net" + "net/http" + "net/url" +) + +// DialError is an error that occurs while dialling a websocket server. +type DialError struct { + *Config + Err error +} + +func (e *DialError) Error() string { + return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() +} + +// NewConfig creates a new WebSocket config for client connection. +func NewConfig(server, origin string) (config *Config, err error) { + config = new(Config) + config.Version = ProtocolVersionHybi13 + config.Location, err = url.ParseRequestURI(server) + if err != nil { + return + } + config.Origin, err = url.ParseRequestURI(origin) + if err != nil { + return + } + config.Header = http.Header(make(map[string][]string)) + return +} + +// NewClient creates a new WebSocket client connection over rwc. +func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + err = hybiClientHandshake(config, br, bw) + if err != nil { + return + } + buf := bufio.NewReadWriter(br, bw) + ws = newHybiClientConn(config, buf, rwc) + return +} + +// Dial opens a new client connection to a WebSocket. +func Dial(url_, protocol, origin string) (ws *Conn, err error) { + config, err := NewConfig(url_, origin) + if err != nil { + return nil, err + } + if protocol != "" { + config.Protocol = []string{protocol} + } + return DialConfig(config) +} + +// DialConfig opens a new client connection to a WebSocket with a config. +func DialConfig(config *Config) (ws *Conn, err error) { + var client net.Conn + if config.Location == nil { + return nil, &DialError{config, ErrBadWebSocketLocation} + } + if config.Origin == nil { + return nil, &DialError{config, ErrBadWebSocketOrigin} + } + switch config.Location.Scheme { + case "ws": + client, err = net.Dial("tcp", config.Location.Host) + + case "wss": + client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig) + + default: + err = ErrBadScheme + } + if err != nil { + goto Error + } + + ws, err = NewClient(config, client) + if err != nil { + goto Error + } + return + +Error: + return nil, &DialError{config, err} +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/exampledial_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/exampledial_test.go new file mode 100644 index 000000000..777a66895 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/exampledial_test.go @@ -0,0 +1,31 @@ +// 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 websocket_test + +import ( + "fmt" + "log" + + "code.google.com/p/go.net/websocket" +) + +// This example demonstrates a trivial client. +func ExampleDial() { + origin := "http://localhost/" + url := "ws://localhost:12345/ws" + ws, err := websocket.Dial(url, "", origin) + if err != nil { + log.Fatal(err) + } + if _, err := ws.Write([]byte("hello, world!\n")); err != nil { + log.Fatal(err) + } + var msg = make([]byte, 512) + var n int + if n, err = ws.Read(msg); err != nil { + log.Fatal(err) + } + fmt.Printf("Received: %s.\n", msg[:n]) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/examplehandler_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/examplehandler_test.go new file mode 100644 index 000000000..47b0bb9b5 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/examplehandler_test.go @@ -0,0 +1,26 @@ +// 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 websocket_test + +import ( + "io" + "net/http" + + "code.google.com/p/go.net/websocket" +) + +// Echo the data received on the WebSocket. +func EchoServer(ws *websocket.Conn) { + io.Copy(ws, ws) +} + +// This example demonstrates a trivial echo server. +func ExampleHandler() { + http.Handle("/echo", websocket.Handler(EchoServer)) + err := http.ListenAndServe(":12345", nil) + if err != nil { + panic("ListenAndServe: " + err.Error()) + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi.go new file mode 100644 index 000000000..f8c0b2e29 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi.go @@ -0,0 +1,564 @@ +// Copyright 2011 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 websocket + +// This file implements a protocol of hybi draft. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 + +import ( + "bufio" + "bytes" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +const ( + websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + closeStatusNormal = 1000 + closeStatusGoingAway = 1001 + closeStatusProtocolError = 1002 + closeStatusUnsupportedData = 1003 + closeStatusFrameTooLarge = 1004 + closeStatusNoStatusRcvd = 1005 + closeStatusAbnormalClosure = 1006 + closeStatusBadMessageData = 1007 + closeStatusPolicyViolation = 1008 + closeStatusTooBigData = 1009 + closeStatusExtensionMismatch = 1010 + + maxControlFramePayloadLength = 125 +) + +var ( + ErrBadMaskingKey = &ProtocolError{"bad masking key"} + ErrBadPongMessage = &ProtocolError{"bad pong message"} + ErrBadClosingStatus = &ProtocolError{"bad closing status"} + ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} + ErrNotImplemented = &ProtocolError{"not implemented"} + + handshakeHeader = map[string]bool{ + "Host": true, + "Upgrade": true, + "Connection": true, + "Sec-Websocket-Key": true, + "Sec-Websocket-Origin": true, + "Sec-Websocket-Version": true, + "Sec-Websocket-Protocol": true, + "Sec-Websocket-Accept": true, + } +) + +// A hybiFrameHeader is a frame header as defined in hybi draft. +type hybiFrameHeader struct { + Fin bool + Rsv [3]bool + OpCode byte + Length int64 + MaskingKey []byte + + data *bytes.Buffer +} + +// A hybiFrameReader is a reader for hybi frame. +type hybiFrameReader struct { + reader io.Reader + + header hybiFrameHeader + pos int64 + length int +} + +func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { + n, err = frame.reader.Read(msg) + if err != nil { + return 0, err + } + if frame.header.MaskingKey != nil { + for i := 0; i < n; i++ { + msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] + frame.pos++ + } + } + return n, err +} + +func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } + +func (frame *hybiFrameReader) HeaderReader() io.Reader { + if frame.header.data == nil { + return nil + } + if frame.header.data.Len() == 0 { + return nil + } + return frame.header.data +} + +func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } + +func (frame *hybiFrameReader) Len() (n int) { return frame.length } + +// A hybiFrameReaderFactory creates new frame reader based on its frame type. +type hybiFrameReaderFactory struct { + *bufio.Reader +} + +// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. +// See Section 5.2 Base Framing protocol for detail. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 +func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { + hybiFrame := new(hybiFrameReader) + frame = hybiFrame + var header []byte + var b byte + // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 + for i := 0; i < 3; i++ { + j := uint(6 - i) + hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 + } + hybiFrame.header.OpCode = header[0] & 0x0f + + // Second byte. Mask/Payload len(7bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + mask := (b & 0x80) != 0 + b &= 0x7f + lengthFields := 0 + switch { + case b <= 125: // Payload length 7bits. + hybiFrame.header.Length = int64(b) + case b == 126: // Payload length 7+16bits + lengthFields = 2 + case b == 127: // Payload length 7+64bits + lengthFields = 8 + } + for i := 0; i < lengthFields; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) + } + if mask { + // Masking key. 4 bytes. + for i := 0; i < 4; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) + } + } + hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) + hybiFrame.header.data = bytes.NewBuffer(header) + hybiFrame.length = len(header) + int(hybiFrame.header.Length) + return +} + +// A HybiFrameWriter is a writer for hybi frame. +type hybiFrameWriter struct { + writer *bufio.Writer + + header *hybiFrameHeader +} + +func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { + var header []byte + var b byte + if frame.header.Fin { + b |= 0x80 + } + for i := 0; i < 3; i++ { + if frame.header.Rsv[i] { + j := uint(6 - i) + b |= 1 << j + } + } + b |= frame.header.OpCode + header = append(header, b) + if frame.header.MaskingKey != nil { + b = 0x80 + } else { + b = 0 + } + lengthFields := 0 + length := len(msg) + switch { + case length <= 125: + b |= byte(length) + case length < 65536: + b |= 126 + lengthFields = 2 + default: + b |= 127 + lengthFields = 8 + } + header = append(header, b) + for i := 0; i < lengthFields; i++ { + j := uint((lengthFields - i - 1) * 8) + b = byte((length >> j) & 0xff) + header = append(header, b) + } + if frame.header.MaskingKey != nil { + if len(frame.header.MaskingKey) != 4 { + return 0, ErrBadMaskingKey + } + header = append(header, frame.header.MaskingKey...) + frame.writer.Write(header) + data := make([]byte, length) + for i := range data { + data[i] = msg[i] ^ frame.header.MaskingKey[i%4] + } + frame.writer.Write(data) + err = frame.writer.Flush() + return length, err + } + frame.writer.Write(header) + frame.writer.Write(msg) + err = frame.writer.Flush() + return length, err +} + +func (frame *hybiFrameWriter) Close() error { return nil } + +type hybiFrameWriterFactory struct { + *bufio.Writer + needMaskingKey bool +} + +func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} + if buf.needMaskingKey { + frameHeader.MaskingKey, err = generateMaskingKey() + if err != nil { + return nil, err + } + } + return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil +} + +type hybiFrameHandler struct { + conn *Conn + payloadType byte +} + +func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { + if handler.conn.IsServerConn() { + // The client MUST mask all frames sent to the server. + if frame.(*hybiFrameReader).header.MaskingKey == nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } else { + // The server MUST NOT mask all frames. + if frame.(*hybiFrameReader).header.MaskingKey != nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } + if header := frame.HeaderReader(); header != nil { + io.Copy(ioutil.Discard, header) + } + switch frame.PayloadType() { + case ContinuationFrame: + frame.(*hybiFrameReader).header.OpCode = handler.payloadType + case TextFrame, BinaryFrame: + handler.payloadType = frame.PayloadType() + case CloseFrame: + return nil, io.EOF + case PingFrame: + pingMsg := make([]byte, maxControlFramePayloadLength) + n, err := io.ReadFull(frame, pingMsg) + if err != nil && err != io.ErrUnexpectedEOF { + return nil, err + } + io.Copy(ioutil.Discard, frame) + n, err = handler.WritePong(pingMsg[:n]) + if err != nil { + return nil, err + } + return nil, nil + case PongFrame: + return nil, ErrNotImplemented + } + return frame, nil +} + +func (handler *hybiFrameHandler) WriteClose(status int) (err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) + if err != nil { + return err + } + msg := make([]byte, 2) + binary.BigEndian.PutUint16(msg, uint16(status)) + _, err = w.Write(msg) + w.Close() + return err +} + +func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + return n, err +} + +// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. +func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + if buf == nil { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + buf = bufio.NewReadWriter(br, bw) + } + ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, + frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, + frameWriterFactory: hybiFrameWriterFactory{ + buf.Writer, request == nil}, + PayloadType: TextFrame, + defaultCloseStatus: closeStatusNormal} + ws.frameHandler = &hybiFrameHandler{conn: ws} + return ws +} + +// generateMaskingKey generates a masking key for a frame. +func generateMaskingKey() (maskingKey []byte, err error) { + maskingKey = make([]byte, 4) + if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { + return + } + return +} + +// generateNonce generates a nonce consisting of a randomly selected 16-byte +// value that has been base64-encoded. +func generateNonce() (nonce []byte) { + key := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, key); err != nil { + panic(err) + } + nonce = make([]byte, 24) + base64.StdEncoding.Encode(nonce, key) + return +} + +// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of +// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. +func getNonceAccept(nonce []byte) (expected []byte, err error) { + h := sha1.New() + if _, err = h.Write(nonce); err != nil { + return + } + if _, err = h.Write([]byte(websocketGUID)); err != nil { + return + } + expected = make([]byte, 28) + base64.StdEncoding.Encode(expected, h.Sum(nil)) + return +} + +// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 +func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { + bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") + + bw.WriteString("Host: " + config.Location.Host + "\r\n") + bw.WriteString("Upgrade: websocket\r\n") + bw.WriteString("Connection: Upgrade\r\n") + nonce := generateNonce() + if config.handshakeData != nil { + nonce = []byte(config.handshakeData["key"]) + } + bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") + bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") + + if config.Version != ProtocolVersionHybi13 { + return ErrBadProtocolVersion + } + + bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") + if len(config.Protocol) > 0 { + bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + err = config.Header.WriteSubset(bw, handshakeHeader) + if err != nil { + return err + } + + bw.WriteString("\r\n") + if err = bw.Flush(); err != nil { + return err + } + + resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) + if err != nil { + return err + } + if resp.StatusCode != 101 { + return ErrBadStatus + } + if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || + strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { + return ErrBadUpgrade + } + expectedAccept, err := getNonceAccept(nonce) + if err != nil { + return err + } + if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { + return ErrChallengeResponse + } + if resp.Header.Get("Sec-WebSocket-Extensions") != "" { + return ErrUnsupportedExtensions + } + offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") + if offeredProtocol != "" { + protocolMatched := false + for i := 0; i < len(config.Protocol); i++ { + if config.Protocol[i] == offeredProtocol { + protocolMatched = true + break + } + } + if !protocolMatched { + return ErrBadWebSocketProtocol + } + config.Protocol = []string{offeredProtocol} + } + + return nil +} + +// newHybiClientConn creates a client WebSocket connection after handshake. +func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { + return newHybiConn(config, buf, rwc, nil) +} + +// A HybiServerHandshaker performs a server handshake using hybi draft protocol. +type hybiServerHandshaker struct { + *Config + accept []byte +} + +func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { + c.Version = ProtocolVersionHybi13 + if req.Method != "GET" { + return http.StatusMethodNotAllowed, ErrBadRequestMethod + } + // HTTP version can be safely ignored. + + if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || + !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { + return http.StatusBadRequest, ErrNotWebSocket + } + + key := req.Header.Get("Sec-Websocket-Key") + if key == "" { + return http.StatusBadRequest, ErrChallengeResponse + } + version := req.Header.Get("Sec-Websocket-Version") + switch version { + case "13": + c.Version = ProtocolVersionHybi13 + default: + return http.StatusBadRequest, ErrBadWebSocketVersion + } + var scheme string + if req.TLS != nil { + scheme = "wss" + } else { + scheme = "ws" + } + c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) + if err != nil { + return http.StatusBadRequest, err + } + protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) + if protocol != "" { + protocols := strings.Split(protocol, ",") + for i := 0; i < len(protocols); i++ { + c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) + } + } + c.accept, err = getNonceAccept([]byte(key)) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusSwitchingProtocols, nil +} + +// Origin parses Origin header in "req". +// If origin is "null", returns (nil, nil). +func Origin(config *Config, req *http.Request) (*url.URL, error) { + var origin string + switch config.Version { + case ProtocolVersionHybi13: + origin = req.Header.Get("Origin") + } + if origin == "null" { + return nil, nil + } + return url.ParseRequestURI(origin) +} + +func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { + if len(c.Protocol) > 0 { + if len(c.Protocol) != 1 { + // You need choose a Protocol in Handshake func in Server. + return ErrBadWebSocketProtocol + } + } + buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") + buf.WriteString("Upgrade: websocket\r\n") + buf.WriteString("Connection: Upgrade\r\n") + buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") + if len(c.Protocol) > 0 { + buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + if c.Header != nil { + err := c.Header.WriteSubset(buf, handshakeHeader) + if err != nil { + return err + } + } + buf.WriteString("\r\n") + return buf.Flush() +} + +func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiServerConn(c.Config, buf, rwc, request) +} + +// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. +func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiConn(config, buf, rwc, request) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi_test.go new file mode 100644 index 000000000..d6a19108a --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/hybi_test.go @@ -0,0 +1,590 @@ +// Copyright 2011 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 websocket + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "testing" +) + +// Test the getNonceAccept function with values in +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 +func TestSecWebSocketAccept(t *testing.T) { + nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") + expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") + accept, err := getNonceAccept(nonce) + if err != nil { + t.Errorf("getNonceAccept: returned error %v", err) + return + } + if !bytes.Equal(expected, accept) { + t.Errorf("getNonceAccept: expected %q got %q", expected, accept) + } +} + +func TestHybiClientHandshake(t *testing.T) { + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= +Sec-WebSocket-Protocol: chat + +`)) + var err error + config := new(Config) + config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") + if err != nil { + t.Fatal("location url", err) + } + config.Origin, err = url.ParseRequestURI("http://example.com") + if err != nil { + t.Fatal("origin url", err) + } + config.Protocol = append(config.Protocol, "chat") + config.Protocol = append(config.Protocol, "superchat") + config.Version = ProtocolVersionHybi13 + + config.handshakeData = map[string]string{ + "key": "dGhlIHNhbXBsZSBub25jZQ==", + } + err = hybiClientHandshake(config, br, bw) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + req, err := http.ReadRequest(bufio.NewReader(b)) + if err != nil { + t.Fatalf("read request: %v", err) + } + if req.Method != "GET" { + t.Errorf("request method expected GET, but got %q", req.Method) + } + if req.URL.Path != "/chat" { + t.Errorf("request path expected /chat, but got %q", req.URL.Path) + } + if req.Proto != "HTTP/1.1" { + t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) + } + if req.Host != "server.example.com" { + t.Errorf("request Host expected server.example.com, but got %v", req.Host) + } + var expectedHeader = map[string]string{ + "Connection": "Upgrade", + "Upgrade": "websocket", + "Sec-Websocket-Key": config.handshakeData["key"], + "Origin": config.Origin.String(), + "Sec-Websocket-Protocol": "chat, superchat", + "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), + } + for k, v := range expectedHeader { + if req.Header.Get(k) != v { + t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) + } + } +} + +func TestHybiClientHandshakeWithHeader(t *testing.T) { + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= +Sec-WebSocket-Protocol: chat + +`)) + var err error + config := new(Config) + config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") + if err != nil { + t.Fatal("location url", err) + } + config.Origin, err = url.ParseRequestURI("http://example.com") + if err != nil { + t.Fatal("origin url", err) + } + config.Protocol = append(config.Protocol, "chat") + config.Protocol = append(config.Protocol, "superchat") + config.Version = ProtocolVersionHybi13 + config.Header = http.Header(make(map[string][]string)) + config.Header.Add("User-Agent", "test") + + config.handshakeData = map[string]string{ + "key": "dGhlIHNhbXBsZSBub25jZQ==", + } + err = hybiClientHandshake(config, br, bw) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + req, err := http.ReadRequest(bufio.NewReader(b)) + if err != nil { + t.Fatalf("read request: %v", err) + } + if req.Method != "GET" { + t.Errorf("request method expected GET, but got %q", req.Method) + } + if req.URL.Path != "/chat" { + t.Errorf("request path expected /chat, but got %q", req.URL.Path) + } + if req.Proto != "HTTP/1.1" { + t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) + } + if req.Host != "server.example.com" { + t.Errorf("request Host expected server.example.com, but got %v", req.Host) + } + var expectedHeader = map[string]string{ + "Connection": "Upgrade", + "Upgrade": "websocket", + "Sec-Websocket-Key": config.handshakeData["key"], + "Origin": config.Origin.String(), + "Sec-Websocket-Protocol": "chat, superchat", + "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), + "User-Agent": "test", + } + for k, v := range expectedHeader { + if req.Header.Get(k) != v { + t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) + } + } +} + +func TestHybiServerHandshake(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + expectedProtocols := []string{"chat", "superchat"} + if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) { + t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + config.Protocol = config.Protocol[:1] + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol: chat", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} + +func TestHybiServerHandshakeNoSubProtocol(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + if len(config.Protocol) != 0 { + t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol)) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} + +func TestHybiServerHandshakeHybiBadVersion(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Sec-WebSocket-Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 9 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != ErrBadWebSocketVersion { + t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) + } + if code != http.StatusBadRequest { + t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) + } +} + +func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { + b := bytes.NewBuffer([]byte{}) + frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} + w, _ := frameWriterFactory.NewFrameWriter(TextFrame) + w.(*hybiFrameWriter).header = frameHeader + _, err := w.Write(testPayload) + w.Close() + if err != nil { + t.Errorf("Write error %q", err) + } + var expectedFrame []byte + expectedFrame = append(expectedFrame, testHeader...) + expectedFrame = append(expectedFrame, testMaskedPayload...) + if !bytes.Equal(expectedFrame, b.Bytes()) { + t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) + } + frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} + r, err := frameReaderFactory.NewFrameReader() + if err != nil { + t.Errorf("Read error %q", err) + } + if header := r.HeaderReader(); header == nil { + t.Errorf("no header") + } else { + actualHeader := make([]byte, r.Len()) + n, err := header.Read(actualHeader) + if err != nil { + t.Errorf("Read header error %q", err) + } else { + if n < len(testHeader) { + t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) + } + if !bytes.Equal(testHeader, actualHeader[:n]) { + t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) + } + } + } + if trailer := r.TrailerReader(); trailer != nil { + t.Errorf("unexpected trailer %q", trailer) + } + frame := r.(*hybiFrameReader) + if frameHeader.Fin != frame.header.Fin || + frameHeader.OpCode != frame.header.OpCode || + len(testPayload) != int(frame.header.Length) { + t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) + } + payload := make([]byte, len(testPayload)) + _, err = r.Read(payload) + if err != nil { + t.Errorf("read %v", err) + } + if !bytes.Equal(testPayload, payload) { + t.Errorf("payload %q vs %q", testPayload, payload) + } +} + +func TestHybiShortTextFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) + + payload = make([]byte, 125) + testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) +} + +func TestHybiShortMaskedTextFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, + MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} + payload := []byte("hello") + maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} + header := []byte{0x81, 0x85} + header = append(header, frameHeader.MaskingKey...) + testHybiFrame(t, header, payload, maskedPayload, frameHeader) +} + +func TestHybiShortBinaryFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) + + payload = make([]byte, 125) + testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) +} + +func TestHybiControlFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) + + frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} + testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) + + frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} + payload = []byte{0x03, 0xe8} // 1000 + testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) +} + +func TestHybiLongFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} + payload := make([]byte, 126) + testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) + + payload = make([]byte, 65535) + testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) + + payload = make([]byte, 65536) + testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) +} + +func TestHybiClientRead(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', + 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping + 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + msg := make([]byte, 512) + n, err := conn.Read(msg) + if err != nil { + t.Errorf("read 1st frame, error %q", err) + } + if n != 5 { + t.Errorf("read 1st frame, expect 5, got %d", n) + } + if !bytes.Equal(wireData[2:7], msg[:n]) { + t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) + } + n, err = conn.Read(msg) + if err != nil { + t.Errorf("read 2nd frame, error %q", err) + } + if n != 5 { + t.Errorf("read 2nd frame, expect 5, got %d", n) + } + if !bytes.Equal(wireData[16:21], msg[:n]) { + t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) + } + n, err = conn.Read(msg) + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } +} + +func TestHybiShortRead(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', + 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping + 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + step := 0 + pos := 0 + expectedPos := []int{2, 5, 16, 19} + expectedLen := []int{3, 2, 3, 2} + for { + msg := make([]byte, 3) + n, err := conn.Read(msg) + if step >= len(expectedPos) { + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } + return + } + pos = expectedPos[step] + endPos := pos + expectedLen[step] + if err != nil { + t.Errorf("read from %d, got error %q", pos, err) + return + } + if n != endPos-pos { + t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) + } + if !bytes.Equal(wireData[pos:endPos], msg[:n]) { + t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) + } + step++ + } +} + +func TestHybiServerRead(t *testing.T) { + wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello + 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello + 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, + 0x9a, 0xec, 0xc6, 0x48, 0x89, // world + } + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) + + expected := [][]byte{[]byte("hello"), []byte("world")} + + msg := make([]byte, 512) + n, err := conn.Read(msg) + if err != nil { + t.Errorf("read 1st frame, error %q", err) + } + if n != 5 { + t.Errorf("read 1st frame, expect 5, got %d", n) + } + if !bytes.Equal(expected[0], msg[:n]) { + t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) + } + + n, err = conn.Read(msg) + if err != nil { + t.Errorf("read 2nd frame, error %q", err) + } + if n != 5 { + t.Errorf("read 2nd frame, expect 5, got %d", n) + } + if !bytes.Equal(expected[1], msg[:n]) { + t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) + } + + n, err = conn.Read(msg) + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } +} + +func TestHybiServerReadWithoutMasking(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) + // server MUST close the connection upon receiving a non-masked frame. + msg := make([]byte, 512) + _, err := conn.Read(msg) + if err != io.EOF { + t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) + } +} + +func TestHybiClientReadWithMasking(t *testing.T) { + wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello + } + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + // client MUST close the connection upon receiving a masked frame. + msg := make([]byte, 512) + _, err := conn.Read(msg) + if err != io.EOF { + t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) + } +} + +// Test the hybiServerHandshaker supports firefox implementation and +// checks Connection request header include (but it's not necessary +// equal to) "upgrade" +func TestHybiServerFirefoxHandshake(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: keep-alive, upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + config.Protocol = []string{"chat"} + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol: chat", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/server.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/server.go new file mode 100644 index 000000000..70322133c --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/server.go @@ -0,0 +1,114 @@ +// 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 websocket + +import ( + "bufio" + "fmt" + "io" + "net/http" +) + +func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { + var hs serverHandshaker = &hybiServerHandshaker{Config: config} + code, err := hs.ReadHandshake(buf.Reader, req) + if err == ErrBadWebSocketVersion { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if err != nil { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if handshake != nil { + err = handshake(config, req) + if err != nil { + code = http.StatusForbidden + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + } + err = hs.AcceptHandshake(buf.Writer) + if err != nil { + code = http.StatusBadRequest + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + conn = hs.NewServerConn(buf, rwc, req) + return +} + +// Server represents a server of a WebSocket. +type Server struct { + // Config is a WebSocket configuration for new WebSocket connection. + Config + + // Handshake is an optional function in WebSocket handshake. + // For example, you can check, or don't check Origin header. + // Another example, you can select config.Protocol. + Handshake func(*Config, *http.Request) error + + // Handler handles a WebSocket connection. + Handler +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.serveWebSocket(w, req) +} + +func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { + rwc, buf, err := w.(http.Hijacker).Hijack() + if err != nil { + panic("Hijack failed: " + err.Error()) + return + } + // The server should abort the WebSocket connection if it finds + // the client did not send a handshake that matches with protocol + // specification. + defer rwc.Close() + conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) + if err != nil { + return + } + if conn == nil { + panic("unexpected nil conn") + } + s.Handler(conn) +} + +// Handler is a simple interface to a WebSocket browser client. +// It checks if Origin header is valid URL by default. +// You might want to verify websocket.Conn.Config().Origin in the func. +// If you use Server instead of Handler, you could call websocket.Origin and +// check the origin in your Handshake func. So, if you want to accept +// non-browser client, which doesn't send Origin header, you could use Server +//. that doesn't check origin in its Handshake. +type Handler func(*Conn) + +func checkOrigin(config *Config, req *http.Request) (err error) { + config.Origin, err = Origin(config, req) + if err == nil && config.Origin == nil { + return fmt.Errorf("null origin") + } + return err +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s := Server{Handler: h, Handshake: checkOrigin} + s.serveWebSocket(w, req) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket.go new file mode 100644 index 000000000..0f4917bf7 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket.go @@ -0,0 +1,411 @@ +// 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 websocket implements a client and server for the WebSocket protocol +// as specified in RFC 6455. +package websocket + +import ( + "bufio" + "crypto/tls" + "encoding/json" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "sync" + "time" +) + +const ( + ProtocolVersionHybi13 = 13 + ProtocolVersionHybi = ProtocolVersionHybi13 + SupportedProtocolVersion = "13" + + ContinuationFrame = 0 + TextFrame = 1 + BinaryFrame = 2 + CloseFrame = 8 + PingFrame = 9 + PongFrame = 10 + UnknownFrame = 255 +) + +// ProtocolError represents WebSocket protocol errors. +type ProtocolError struct { + ErrorString string +} + +func (err *ProtocolError) Error() string { return err.ErrorString } + +var ( + ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} + ErrBadScheme = &ProtocolError{"bad scheme"} + ErrBadStatus = &ProtocolError{"bad status"} + ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} + ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} + ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} + ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} + ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} + ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} + ErrBadFrame = &ProtocolError{"bad frame"} + ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} + ErrNotWebSocket = &ProtocolError{"not websocket protocol"} + ErrBadRequestMethod = &ProtocolError{"bad method"} + ErrNotSupported = &ProtocolError{"not supported"} +) + +// Addr is an implementation of net.Addr for WebSocket. +type Addr struct { + *url.URL +} + +// Network returns the network type for a WebSocket, "websocket". +func (addr *Addr) Network() string { return "websocket" } + +// Config is a WebSocket configuration +type Config struct { + // A WebSocket server address. + Location *url.URL + + // A Websocket client origin. + Origin *url.URL + + // WebSocket subprotocols. + Protocol []string + + // WebSocket protocol version. + Version int + + // TLS config for secure WebSocket (wss). + TlsConfig *tls.Config + + // Additional header fields to be sent in WebSocket opening handshake. + Header http.Header + + handshakeData map[string]string +} + +// serverHandshaker is an interface to handle WebSocket server side handshake. +type serverHandshaker interface { + // ReadHandshake reads handshake request message from client. + // Returns http response code and error if any. + ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) + + // AcceptHandshake accepts the client handshake request and sends + // handshake response back to client. + AcceptHandshake(buf *bufio.Writer) (err error) + + // NewServerConn creates a new WebSocket connection. + NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) +} + +// frameReader is an interface to read a WebSocket frame. +type frameReader interface { + // Reader is to read payload of the frame. + io.Reader + + // PayloadType returns payload type. + PayloadType() byte + + // HeaderReader returns a reader to read header of the frame. + HeaderReader() io.Reader + + // TrailerReader returns a reader to read trailer of the frame. + // If it returns nil, there is no trailer in the frame. + TrailerReader() io.Reader + + // Len returns total length of the frame, including header and trailer. + Len() int +} + +// frameReaderFactory is an interface to creates new frame reader. +type frameReaderFactory interface { + NewFrameReader() (r frameReader, err error) +} + +// frameWriter is an interface to write a WebSocket frame. +type frameWriter interface { + // Writer is to write payload of the frame. + io.WriteCloser +} + +// frameWriterFactory is an interface to create new frame writer. +type frameWriterFactory interface { + NewFrameWriter(payloadType byte) (w frameWriter, err error) +} + +type frameHandler interface { + HandleFrame(frame frameReader) (r frameReader, err error) + WriteClose(status int) (err error) +} + +// Conn represents a WebSocket connection. +type Conn struct { + config *Config + request *http.Request + + buf *bufio.ReadWriter + rwc io.ReadWriteCloser + + rio sync.Mutex + frameReaderFactory + frameReader + + wio sync.Mutex + frameWriterFactory + + frameHandler + PayloadType byte + defaultCloseStatus int +} + +// Read implements the io.Reader interface: +// it reads data of a frame from the WebSocket connection. +// if msg is not large enough for the frame data, it fills the msg and next Read +// will read the rest of the frame data. +// it reads Text frame or Binary frame. +func (ws *Conn) Read(msg []byte) (n int, err error) { + ws.rio.Lock() + defer ws.rio.Unlock() +again: + if ws.frameReader == nil { + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return 0, err + } + ws.frameReader, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return 0, err + } + if ws.frameReader == nil { + goto again + } + } + n, err = ws.frameReader.Read(msg) + if err == io.EOF { + if trailer := ws.frameReader.TrailerReader(); trailer != nil { + io.Copy(ioutil.Discard, trailer) + } + ws.frameReader = nil + goto again + } + return n, err +} + +// Write implements the io.Writer interface: +// it writes data as a frame to the WebSocket connection. +func (ws *Conn) Write(msg []byte) (n int, err error) { + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + if err != nil { + return n, err + } + return n, err +} + +// Close implements the io.Closer interface. +func (ws *Conn) Close() error { + err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) + if err != nil { + return err + } + return ws.rwc.Close() +} + +func (ws *Conn) IsClientConn() bool { return ws.request == nil } +func (ws *Conn) IsServerConn() bool { return ws.request != nil } + +// LocalAddr returns the WebSocket Origin for the connection for client, or +// the WebSocket location for server. +func (ws *Conn) LocalAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Origin} + } + return &Addr{ws.config.Location} +} + +// RemoteAddr returns the WebSocket location for the connection for client, or +// the Websocket Origin for server. +func (ws *Conn) RemoteAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Location} + } + return &Addr{ws.config.Origin} +} + +var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") + +// SetDeadline sets the connection's network read & write deadlines. +func (ws *Conn) SetDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetDeadline(t) + } + return errSetDeadline +} + +// SetReadDeadline sets the connection's network read deadline. +func (ws *Conn) SetReadDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetReadDeadline(t) + } + return errSetDeadline +} + +// SetWriteDeadline sets the connection's network write deadline. +func (ws *Conn) SetWriteDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetWriteDeadline(t) + } + return errSetDeadline +} + +// Config returns the WebSocket config. +func (ws *Conn) Config() *Config { return ws.config } + +// Request returns the http request upgraded to the WebSocket. +// It is nil for client side. +func (ws *Conn) Request() *http.Request { return ws.request } + +// Codec represents a symmetric pair of functions that implement a codec. +type Codec struct { + Marshal func(v interface{}) (data []byte, payloadType byte, err error) + Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) +} + +// Send sends v marshaled by cd.Marshal as single frame to ws. +func (cd Codec) Send(ws *Conn, v interface{}) (err error) { + data, payloadType, err := cd.Marshal(v) + if err != nil { + return err + } + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) + if err != nil { + return err + } + _, err = w.Write(data) + w.Close() + return err +} + +// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. +func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { + ws.rio.Lock() + defer ws.rio.Unlock() + if ws.frameReader != nil { + _, err = io.Copy(ioutil.Discard, ws.frameReader) + if err != nil { + return err + } + ws.frameReader = nil + } +again: + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return err + } + frame, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return err + } + if frame == nil { + goto again + } + payloadType := frame.PayloadType() + data, err := ioutil.ReadAll(frame) + if err != nil { + return err + } + return cd.Unmarshal(data, payloadType, v) +} + +func marshal(v interface{}) (msg []byte, payloadType byte, err error) { + switch data := v.(type) { + case string: + return []byte(data), TextFrame, nil + case []byte: + return data, BinaryFrame, nil + } + return nil, UnknownFrame, ErrNotSupported +} + +func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + switch data := v.(type) { + case *string: + *data = string(msg) + return nil + case *[]byte: + *data = msg + return nil + } + return ErrNotSupported +} + +/* +Message is a codec to send/receive text/binary data in a frame on WebSocket connection. +To send/receive text frame, use string type. +To send/receive binary frame, use []byte type. + +Trivial usage: + + import "websocket" + + // receive text frame + var message string + websocket.Message.Receive(ws, &message) + + // send text frame + message = "hello" + websocket.Message.Send(ws, message) + + // receive binary frame + var data []byte + websocket.Message.Receive(ws, &data) + + // send binary frame + data = []byte{0, 1, 2} + websocket.Message.Send(ws, data) + +*/ +var Message = Codec{marshal, unmarshal} + +func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { + msg, err = json.Marshal(v) + return msg, TextFrame, err +} + +func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + return json.Unmarshal(msg, v) +} + +/* +JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. + +Trivial usage: + + import "websocket" + + type T struct { + Msg string + Count int + } + + // receive JSON type T + var data T + websocket.JSON.Receive(ws, &data) + + // send JSON type T + websocket.JSON.Send(ws, data) +*/ +var JSON = Codec{jsonMarshal, jsonUnmarshal} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket_test.go new file mode 100644 index 000000000..48f14b696 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/websocket/websocket_test.go @@ -0,0 +1,341 @@ +// 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 websocket + +import ( + "bytes" + "fmt" + "io" + "log" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "sync" + "testing" +) + +var serverAddr string +var once sync.Once + +func echoServer(ws *Conn) { io.Copy(ws, ws) } + +type Count struct { + S string + N int +} + +func countServer(ws *Conn) { + for { + var count Count + err := JSON.Receive(ws, &count) + if err != nil { + return + } + count.N++ + count.S = strings.Repeat(count.S, count.N) + err = JSON.Send(ws, count) + if err != nil { + return + } + } +} + +func subProtocolHandshake(config *Config, req *http.Request) error { + for _, proto := range config.Protocol { + if proto == "chat" { + config.Protocol = []string{proto} + return nil + } + } + return ErrBadWebSocketProtocol +} + +func subProtoServer(ws *Conn) { + for _, proto := range ws.Config().Protocol { + io.WriteString(ws, proto) + } +} + +func startServer() { + http.Handle("/echo", Handler(echoServer)) + http.Handle("/count", Handler(countServer)) + subproto := Server{ + Handshake: subProtocolHandshake, + Handler: Handler(subProtoServer), + } + http.Handle("/subproto", subproto) + server := httptest.NewServer(nil) + serverAddr = server.Listener.Addr().String() + log.Print("Test WebSocket server listening on ", serverAddr) +} + +func newConfig(t *testing.T, path string) *Config { + config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") + return config +} + +func TestEcho(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + msg := []byte("hello, world\n") + if _, err := conn.Write(msg); err != nil { + t.Errorf("Write: %v", err) + } + var actual_msg = make([]byte, 512) + n, err := conn.Read(actual_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + actual_msg = actual_msg[0:n] + if !bytes.Equal(msg, actual_msg) { + t.Errorf("Echo: expected %q got %q", msg, actual_msg) + } + conn.Close() +} + +func TestAddr(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + ra := conn.RemoteAddr().String() + if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { + t.Errorf("Bad remote addr: %v", ra) + } + la := conn.LocalAddr().String() + if !strings.HasPrefix(la, "http://") { + t.Errorf("Bad local addr: %v", la) + } + conn.Close() +} + +func TestCount(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/count"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + var count Count + count.S = "hello" + if err := JSON.Send(conn, count); err != nil { + t.Errorf("Write: %v", err) + } + if err := JSON.Receive(conn, &count); err != nil { + t.Errorf("Read: %v", err) + } + if count.N != 1 { + t.Errorf("count: expected %d got %d", 1, count.N) + } + if count.S != "hello" { + t.Errorf("count: expected %q got %q", "hello", count.S) + } + if err := JSON.Send(conn, count); err != nil { + t.Errorf("Write: %v", err) + } + if err := JSON.Receive(conn, &count); err != nil { + t.Errorf("Read: %v", err) + } + if count.N != 2 { + t.Errorf("count: expected %d got %d", 2, count.N) + } + if count.S != "hellohello" { + t.Errorf("count: expected %q got %q", "hellohello", count.S) + } + conn.Close() +} + +func TestWithQuery(t *testing.T) { + once.Do(startServer) + + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + + config := newConfig(t, "/echo") + config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) + if err != nil { + t.Fatal("location url", err) + } + + ws, err := NewClient(config, client) + if err != nil { + t.Errorf("WebSocket handshake: %v", err) + return + } + ws.Close() +} + +func testWithProtocol(t *testing.T, subproto []string) (string, error) { + once.Do(startServer) + + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + + config := newConfig(t, "/subproto") + config.Protocol = subproto + + ws, err := NewClient(config, client) + if err != nil { + return "", err + } + msg := make([]byte, 16) + n, err := ws.Read(msg) + if err != nil { + return "", err + } + ws.Close() + return string(msg[:n]), nil +} + +func TestWithProtocol(t *testing.T) { + proto, err := testWithProtocol(t, []string{"chat"}) + if err != nil { + t.Errorf("SubProto: unexpected error: %v", err) + } + if proto != "chat" { + t.Errorf("SubProto: expected %q, got %q", "chat", proto) + } +} + +func TestWithTwoProtocol(t *testing.T) { + proto, err := testWithProtocol(t, []string{"test", "chat"}) + if err != nil { + t.Errorf("SubProto: unexpected error: %v", err) + } + if proto != "chat" { + t.Errorf("SubProto: expected %q, got %q", "chat", proto) + } +} + +func TestWithBadProtocol(t *testing.T) { + _, err := testWithProtocol(t, []string{"test"}) + if err != ErrBadStatus { + t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err) + } +} + +func TestHTTP(t *testing.T) { + once.Do(startServer) + + // If the client did not send a handshake that matches the protocol + // specification, the server MUST return an HTTP response with an + // appropriate error code (such as 400 Bad Request) + resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) + if err != nil { + t.Errorf("Get: error %#v", err) + return + } + if resp == nil { + t.Error("Get: resp is null") + return + } + if resp.StatusCode != http.StatusBadRequest { + t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) + } +} + +func TestTrailingSpaces(t *testing.T) { + // http://code.google.com/p/go/issues/detail?id=955 + // The last runs of this create keys with trailing spaces that should not be + // generated by the client. + once.Do(startServer) + config := newConfig(t, "/echo") + for i := 0; i < 30; i++ { + // body + ws, err := DialConfig(config) + if err != nil { + t.Errorf("Dial #%d failed: %v", i, err) + break + } + ws.Close() + } +} + +func TestDialConfigBadVersion(t *testing.T) { + once.Do(startServer) + config := newConfig(t, "/echo") + config.Version = 1234 + + _, err := DialConfig(config) + + if dialerr, ok := err.(*DialError); ok { + if dialerr.Err != ErrBadProtocolVersion { + t.Errorf("dial expected err %q but got %q", ErrBadProtocolVersion, dialerr.Err) + } + } +} + +func TestSmallBuffer(t *testing.T) { + // http://code.google.com/p/go/issues/detail?id=1145 + // Read should be able to handle reading a fragment of a frame. + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + msg := []byte("hello, world\n") + if _, err := conn.Write(msg); err != nil { + t.Errorf("Write: %v", err) + } + var small_msg = make([]byte, 8) + n, err := conn.Read(small_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + if !bytes.Equal(msg[:len(small_msg)], small_msg) { + t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) + } + var second_msg = make([]byte, len(msg)) + n, err = conn.Read(second_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + second_msg = second_msg[0:n] + if !bytes.Equal(msg[len(small_msg):], second_msg) { + t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) + } + conn.Close() +} diff --git a/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/decode.go b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/decode.go new file mode 100644 index 000000000..d93c1b9db --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/decode.go @@ -0,0 +1,124 @@ +// Copyright 2011 The Snappy-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 snappy + +import ( + "encoding/binary" + "errors" +) + +// ErrCorrupt reports that the input is invalid. +var ErrCorrupt = errors.New("snappy: corrupt input") + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n == 0 { + return 0, 0, ErrCorrupt + } + if uint64(int(v)) != v { + return 0, 0, errors.New("snappy: decoded block is too large") + } + return int(v), n, nil +} + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil dst. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if len(dst) < dLen { + dst = make([]byte, dLen) + } + + var d, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint(src[s] >> 2) + switch { + case x < 60: + s += 1 + case x == 60: + s += 2 + if s > len(src) { + return nil, ErrCorrupt + } + x = uint(src[s-1]) + case x == 61: + s += 3 + if s > len(src) { + return nil, ErrCorrupt + } + x = uint(src[s-2]) | uint(src[s-1])<<8 + case x == 62: + s += 4 + if s > len(src) { + return nil, ErrCorrupt + } + x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16 + case x == 63: + s += 5 + if s > len(src) { + return nil, ErrCorrupt + } + x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24 + } + length = int(x + 1) + if length <= 0 { + return nil, errors.New("snappy: unsupported literal length") + } + if length > len(dst)-d || length > len(src)-s { + return nil, ErrCorrupt + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if s > len(src) { + return nil, ErrCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(src[s-2])&0xe0<<3 | int(src[s-1]) + + case tagCopy2: + s += 3 + if s > len(src) { + return nil, ErrCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(src[s-2]) | int(src[s-1])<<8 + + case tagCopy4: + return nil, errors.New("snappy: unsupported COPY_4 tag") + } + + end := d + length + if offset > d || end > len(dst) { + return nil, ErrCorrupt + } + for ; d < end; d++ { + dst[d] = dst[d-offset] + } + } + if d != dLen { + return nil, ErrCorrupt + } + return dst[:d], nil +} diff --git a/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/encode.go b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/encode.go new file mode 100644 index 000000000..b2371db11 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/encode.go @@ -0,0 +1,174 @@ +// Copyright 2011 The Snappy-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 snappy + +import ( + "encoding/binary" +) + +// We limit how far copy back-references can go, the same as the C++ code. +const maxOffset = 1 << 15 + +// emitLiteral writes a literal chunk and returns the number of bytes written. +func emitLiteral(dst, lit []byte) int { + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[0] = 60<<2 | tagLiteral + dst[1] = uint8(n) + i = 2 + case n < 1<<16: + dst[0] = 61<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + i = 3 + case n < 1<<24: + dst[0] = 62<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + dst[3] = uint8(n >> 16) + i = 4 + case int64(n) < 1<<32: + dst[0] = 63<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + dst[3] = uint8(n >> 16) + dst[4] = uint8(n >> 24) + i = 5 + default: + panic("snappy: source buffer is too long") + } + if copy(dst[i:], lit) != len(lit) { + panic("snappy: destination buffer is too short") + } + return i + len(lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +func emitCopy(dst []byte, offset, length int) int { + i := 0 + for length > 0 { + x := length - 4 + if 0 <= x && x < 1<<3 && offset < 1<<11 { + dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1 + dst[i+1] = uint8(offset) + i += 2 + break + } + + x = length + if x > 1<<6 { + x = 1 << 6 + } + dst[i+0] = uint8(x-1)<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= x + } + return i +} + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil dst. +func Encode(dst, src []byte) ([]byte, error) { + if n := MaxEncodedLen(len(src)); len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + // Return early if src is short. + if len(src) <= 4 { + if len(src) != 0 { + d += emitLiteral(dst[d:], src) + } + return dst[:d], nil + } + + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + const maxTableSize = 1 << 14 + shift, tableSize := uint(32-8), 1<<8 + for tableSize < maxTableSize && tableSize < len(src) { + shift-- + tableSize *= 2 + } + var table [maxTableSize]int + + // Iterate over the source bytes. + var ( + s int // The iterator position. + t int // The last position with the same hash as s. + lit int // The start position of any pending literal bytes. + ) + for s+3 < len(src) { + // Update the hash table. + b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] + h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 + p := &table[(h*0x1e35a7bd)>>shift] + // We need to to store values in [-1, inf) in table. To save + // some initialization time, (re)use the table's zero value + // and shift the values against this zero: add 1 on writes, + // subtract 1 on reads. + t, *p = *p-1, s+1 + // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. + if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { + s++ + continue + } + // Otherwise, we have a match. First, emit any pending literal bytes. + if lit != s { + d += emitLiteral(dst[d:], src[lit:s]) + } + // Extend the match to be as long as possible. + s0 := s + s, t = s+4, t+4 + for s < len(src) && src[s] == src[t] { + s++ + t++ + } + // Emit the copied bytes. + d += emitCopy(dst[d:], s-t, s-s0) + lit = s + } + + // Emit any final pending literal bytes and return. + if lit != len(src) { + d += emitLiteral(dst[d:], src[lit:]) + } + return dst[:d], nil +} + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +func MaxEncodedLen(srcLen int) int { + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // That is, 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + return 32 + srcLen + srcLen/6 +} diff --git a/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy.go b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy.go new file mode 100644 index 000000000..2f1b790d0 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy.go @@ -0,0 +1,38 @@ +// Copyright 2011 The Snappy-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 snappy implements the snappy block-based compression format. +// It aims for very high speeds and reasonable compression. +// +// The C++ snappy implementation is at http://code.google.com/p/snappy/ +package snappy + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer supported. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) diff --git a/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy_test.go b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy_test.go new file mode 100644 index 000000000..7ba839244 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/snappy-go/snappy/snappy_test.go @@ -0,0 +1,261 @@ +// Copyright 2011 The Snappy-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 snappy + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "os" + "path/filepath" + "strings" + "testing" +) + +var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks") + +func roundtrip(b, ebuf, dbuf []byte) error { + e, err := Encode(ebuf, b) + if err != nil { + return fmt.Errorf("encoding error: %v", err) + } + d, err := Decode(dbuf, e) + if err != nil { + return fmt.Errorf("decoding error: %v", err) + } + if !bytes.Equal(b, d) { + return fmt.Errorf("roundtrip mismatch:\n\twant %v\n\tgot %v", b, d) + } + return nil +} + +func TestEmpty(t *testing.T) { + if err := roundtrip(nil, nil, nil); err != nil { + t.Fatal(err) + } +} + +func TestSmallCopy(t *testing.T) { + for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { + for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { + for i := 0; i < 32; i++ { + s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb" + if err := roundtrip([]byte(s), ebuf, dbuf); err != nil { + t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err) + } + } + } + } +} + +func TestSmallRand(t *testing.T) { + rand.Seed(27354294) + for n := 1; n < 20000; n += 23 { + b := make([]byte, n) + for i, _ := range b { + b[i] = uint8(rand.Uint32()) + } + if err := roundtrip(b, nil, nil); err != nil { + t.Fatal(err) + } + } +} + +func TestSmallRegular(t *testing.T) { + for n := 1; n < 20000; n += 23 { + b := make([]byte, n) + for i, _ := range b { + b[i] = uint8(i%10 + 'a') + } + if err := roundtrip(b, nil, nil); err != nil { + t.Fatal(err) + } + } +} + +func benchDecode(b *testing.B, src []byte) { + encoded, err := Encode(nil, src) + if err != nil { + b.Fatal(err) + } + // Bandwidth is in amount of uncompressed data. + b.SetBytes(int64(len(src))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(src, encoded) + } +} + +func benchEncode(b *testing.B, src []byte) { + // Bandwidth is in amount of uncompressed data. + b.SetBytes(int64(len(src))) + dst := make([]byte, MaxEncodedLen(len(src))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(dst, src) + } +} + +func readFile(b *testing.B, filename string) []byte { + src, err := ioutil.ReadFile(filename) + if err != nil { + b.Fatalf("failed reading %s: %s", filename, err) + } + if len(src) == 0 { + b.Fatalf("%s has zero length", filename) + } + return src +} + +// expand returns a slice of length n containing repeated copies of src. +func expand(src []byte, n int) []byte { + dst := make([]byte, n) + for x := dst; len(x) > 0; { + i := copy(x, src) + x = x[i:] + } + return dst +} + +func benchWords(b *testing.B, n int, decode bool) { + // Note: the file is OS-language dependent so the resulting values are not + // directly comparable for non-US-English OS installations. + data := expand(readFile(b, "/usr/share/dict/words"), n) + if decode { + benchDecode(b, data) + } else { + benchEncode(b, data) + } +} + +func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) } +func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) } +func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) } +func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) } +func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) } +func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) } +func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) } +func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) } + +// testFiles' values are copied directly from +// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc. +// The label field is unused in snappy-go. +var testFiles = []struct { + label string + filename string +}{ + {"html", "html"}, + {"urls", "urls.10K"}, + {"jpg", "house.jpg"}, + {"pdf", "mapreduce-osdi-1.pdf"}, + {"html4", "html_x_4"}, + {"cp", "cp.html"}, + {"c", "fields.c"}, + {"lsp", "grammar.lsp"}, + {"xls", "kennedy.xls"}, + {"txt1", "alice29.txt"}, + {"txt2", "asyoulik.txt"}, + {"txt3", "lcet10.txt"}, + {"txt4", "plrabn12.txt"}, + {"bin", "ptt5"}, + {"sum", "sum"}, + {"man", "xargs.1"}, + {"pb", "geo.protodata"}, + {"gaviota", "kppkn.gtb"}, +} + +// The test data files are present at this canonical URL. +const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/" + +func downloadTestdata(basename string) (errRet error) { + filename := filepath.Join("testdata", basename) + f, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to create %s: %s", filename, err) + } + defer f.Close() + defer func() { + if errRet != nil { + os.Remove(filename) + } + }() + resp, err := http.Get(baseURL + basename) + if err != nil { + return fmt.Errorf("failed to download %s: %s", baseURL+basename, err) + } + defer resp.Body.Close() + _, err = io.Copy(f, resp.Body) + if err != nil { + return fmt.Errorf("failed to write %s: %s", filename, err) + } + return nil +} + +func benchFile(b *testing.B, n int, decode bool) { + filename := filepath.Join("testdata", testFiles[n].filename) + if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 { + if !*download { + b.Fatal("test data not found; skipping benchmark without the -download flag") + } + // Download the official snappy C++ implementation reference test data + // files for benchmarking. + if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) { + b.Fatalf("failed to create testdata: %s", err) + } + for _, tf := range testFiles { + if err := downloadTestdata(tf.filename); err != nil { + b.Fatalf("failed to download testdata: %s", err) + } + } + } + data := readFile(b, filename) + if decode { + benchDecode(b, data) + } else { + benchEncode(b, data) + } +} + +// Naming convention is kept similar to what snappy's C++ implementation uses. +func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) } +func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) } +func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) } +func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) } +func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) } +func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) } +func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) } +func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) } +func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) } +func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) } +func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) } +func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) } +func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) } +func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) } +func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) } +func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) } +func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) } +func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) } +func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) } +func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) } +func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) } +func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) } +func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) } +func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) } +func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) } +func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) } +func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) } +func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) } +func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) } +func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) } +func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) } +func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) } +func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) } +func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) } +func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) } +func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) } diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitignore b/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitignore new file mode 100644 index 000000000..5d02b54e5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitignore @@ -0,0 +1,5 @@ +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitmodules b/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitmodules new file mode 100644 index 000000000..054c7d628 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/.gitmodules @@ -0,0 +1,3 @@ +[submodule "serp"] + path = serpent + url = https://github.com/ethereum/serpent.git diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/README.md b/Godeps/_workspace/src/github.com/ethereum/serpent-go/README.md new file mode 100644 index 000000000..404f1b380 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/README.md @@ -0,0 +1,12 @@ +[serpent](https://github.com/ethereum/serpent) go bindings. + +## Build instructions + +``` +go get -d github.com/ethereum/serpent-go +cd $GOPATH/src/github.com/ethereum/serpent-go +git submodule init +git submodule update +``` + +You're now ready to go :-) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/all.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/all.cpp new file mode 100644 index 000000000..80032f900 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/all.cpp @@ -0,0 +1,16 @@ +#include "serpent/bignum.cpp" +#include "serpent/util.cpp" +#include "serpent/tokenize.cpp" +#include "serpent/parser.cpp" +#include "serpent/compiler.cpp" +#include "serpent/funcs.cpp" +#include "serpent/lllparser.cpp" +#include "serpent/rewriter.cpp" + +#include "serpent/opcodes.cpp" +#include "serpent/optimize.cpp" +#include "serpent/functions.cpp" +#include "serpent/preprocess.cpp" +#include "serpent/rewriteutils.cpp" + +#include "cpp/api.cpp" diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.cpp new file mode 100644 index 000000000..bd2c85c7d --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.cpp @@ -0,0 +1,26 @@ +#include + +#include "serpent/lllparser.h" +#include "serpent/bignum.h" +#include "serpent/util.h" +#include "serpent/tokenize.h" +#include "serpent/parser.h" +#include "serpent/compiler.h" + +#include "cpp/api.h" + +const char *compileGo(char *code, int *err) +{ + try { + std::string c = binToHex(compile(std::string(code))); + + return c.c_str(); + } + catch(std::string &error) { + *err = 1; + return error.c_str(); + } + catch(...) { + return "Unknown error"; + } +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.h new file mode 100644 index 000000000..235b5eb4a --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/cpp/api.h @@ -0,0 +1,14 @@ +#ifndef CPP_API_H +#define CPP_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +const char *compileGo(char *code, int *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent.go b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent.go new file mode 100644 index 000000000..39b60eed7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent.go @@ -0,0 +1,27 @@ +package serpent + +// #cgo CXXFLAGS: -I. -Ilangs/ -std=c++0x -Wall -fno-strict-aliasing +// #cgo LDFLAGS: -lstdc++ +// +// #include "cpp/api.h" +// +import "C" + +import ( + "encoding/hex" + "errors" + "unsafe" +) + +func Compile(str string) ([]byte, error) { + var err C.int + out := C.GoString(C.compileGo(C.CString(str), (*C.int)(unsafe.Pointer(&err)))) + + if err == C.int(1) { + return nil, errors.New(out) + } + + bytes, _ := hex.DecodeString(out) + + return bytes, nil +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/.gitignore b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/.gitignore new file mode 100644 index 000000000..72b65e446 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/.gitignore @@ -0,0 +1,12 @@ +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ +*.o +serpent +libserpent.a +pyserpent.so +dist +*.egg-info diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/MANIFEST.in b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/MANIFEST.in new file mode 100644 index 000000000..5f5766ced --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/MANIFEST.in @@ -0,0 +1,5 @@ +include *.cpp +include *.h +include *py +include README.md +include Makefile diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/Makefile b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/Makefile new file mode 100644 index 000000000..28c38728e --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/Makefile @@ -0,0 +1,55 @@ +PLATFORM_OPTS = +PYTHON = /usr/include/python2.7 +CXXFLAGS = -fPIC +# -g3 -O0 +BOOST_INC = /usr/include +BOOST_LIB = /usr/lib +TARGET = pyserpent +COMMON_OBJS = bignum.o util.o tokenize.o lllparser.o parser.o opcodes.o optimize.o functions.o rewriteutils.o preprocess.o rewriter.o compiler.o funcs.o +HEADERS = bignum.h util.h tokenize.h lllparser.h parser.h opcodes.h functions.h optimize.h rewriteutils.h preprocess.h rewriter.h compiler.h funcs.h +PYTHON_VERSION = 2.7 + +serpent : serpentc lib + +lib: + ar rvs libserpent.a $(COMMON_OBJS) + g++ $(CXXFLAGS) -shared $(COMMON_OBJS) -o libserpent.so + +serpentc: $(COMMON_OBJS) cmdline.o + rm -rf serpent + g++ -Wall $(COMMON_OBJS) cmdline.o -o serpent + +bignum.o : bignum.cpp bignum.h + +opcodes.o : opcodes.cpp opcodes.h + +util.o : util.cpp util.h bignum.o + +tokenize.o : tokenize.cpp tokenize.h util.o + +lllparser.o : lllparser.cpp lllparser.h tokenize.o util.o + +parser.o : parser.cpp parser.h tokenize.o util.o + +rewriter.o : rewriter.cpp rewriter.h lllparser.o util.o rewriteutils.o preprocess.o opcodes.o functions.o + +preprocessor.o: rewriteutils.o functions.o + +compiler.o : compiler.cpp compiler.h util.o + +funcs.o : funcs.cpp funcs.h + +cmdline.o: cmdline.cpp + +pyext.o: pyext.cpp + +clean: + rm -f serpent *\.o libserpent.a libserpent.so + +install: + cp serpent /usr/local/bin + cp libserpent.a /usr/local/lib + cp libserpent.so /usr/local/lib + rm -rf /usr/local/include/libserpent + mkdir -p /usr/local/include/libserpent + cp $(HEADERS) /usr/local/include/libserpent diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/README.md b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/README.md new file mode 100644 index 000000000..03dfcc15f --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/README.md @@ -0,0 +1,3 @@ +Installation: + +```make && sudo make install``` diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.cpp new file mode 100644 index 000000000..108b1eb04 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include "bignum.h" + +//Integer to string conversion +std::string unsignedToDecimal(unsigned branch) { + if (branch < 10) return nums.substr(branch, 1); + else return unsignedToDecimal(branch / 10) + nums.substr(branch % 10,1); +} + +//Add two strings representing decimal values +std::string decimalAdd(std::string a, std::string b) { + std::string o = a; + while (b.length() < a.length()) b = "0" + b; + while (o.length() < b.length()) o = "0" + o; + bool carry = false; + for (int i = o.length() - 1; i >= 0; i--) { + o[i] = o[i] + b[i] - '0'; + if (carry) o[i]++; + if (o[i] > '9') { + o[i] -= 10; + carry = true; + } + else carry = false; + } + if (carry) o = "1" + o; + return o; +} + +//Helper function for decimalMul +std::string decimalDigitMul(std::string a, int dig) { + if (dig == 0) return "0"; + else return decimalAdd(a, decimalDigitMul(a, dig - 1)); +} + +//Multiply two strings representing decimal values +std::string decimalMul(std::string a, std::string b) { + std::string o = "0"; + for (unsigned i = 0; i < b.length(); i++) { + std::string n = decimalDigitMul(a, b[i] - '0'); + if (n != "0") { + for (unsigned j = i + 1; j < b.length(); j++) n += "0"; + } + o = decimalAdd(o, n); + } + return o; +} + +//Modexp +std::string decimalModExp(std::string b, std::string e, std::string m) { + if (e == "0") return "1"; + else if (e == "1") return b; + else if (decimalMod(e, "2") == "0") { + std::string o = decimalModExp(b, decimalDiv(e, "2"), m); + return decimalMod(decimalMul(o, o), m); + } + else { + std::string o = decimalModExp(b, decimalDiv(e, "2"), m); + return decimalMod(decimalMul(decimalMul(o, o), b), m); + } +} + +//Is a greater than b? Flag allows equality +bool decimalGt(std::string a, std::string b, bool eqAllowed) { + if (a == b) return eqAllowed; + return (a.length() > b.length()) || (a.length() >= b.length() && a > b); +} + +//Subtract the two strings representing decimal values +std::string decimalSub(std::string a, std::string b) { + if (b == "0") return a; + if (b == a) return "0"; + while (b.length() < a.length()) b = "0" + b; + std::string c = b; + for (unsigned i = 0; i < c.length(); i++) c[i] = '0' + ('9' - c[i]); + std::string o = decimalAdd(decimalAdd(a, c).substr(1), "1"); + while (o.size() > 1 && o[0] == '0') o = o.substr(1); + return o; +} + +//Divide the two strings representing decimal values +std::string decimalDiv(std::string a, std::string b) { + std::string c = b; + if (decimalGt(c, a)) return "0"; + int zeroes = -1; + while (decimalGt(a, c, true)) { + zeroes += 1; + c = c + "0"; + } + c = c.substr(0, c.size() - 1); + std::string quot = "0"; + while (decimalGt(a, c, true)) { + a = decimalSub(a, c); + quot = decimalAdd(quot, "1"); + } + for (int i = 0; i < zeroes; i++) quot += "0"; + return decimalAdd(quot, decimalDiv(a, b)); +} + +//Modulo the two strings representing decimal values +std::string decimalMod(std::string a, std::string b) { + return decimalSub(a, decimalMul(decimalDiv(a, b), b)); +} + +//String to int conversion +unsigned decimalToUnsigned(std::string a) { + if (a.size() == 0) return 0; + else return (a[a.size() - 1] - '0') + + decimalToUnsigned(a.substr(0,a.size()-1)) * 10; +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.h new file mode 100644 index 000000000..99571acd2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/bignum.h @@ -0,0 +1,41 @@ +#ifndef ETHSERP_BIGNUM +#define ETHSERP_BIGNUM + +const std::string nums = "0123456789"; + +const std::string tt256 = +"115792089237316195423570985008687907853269984665640564039457584007913129639936" +; + +const std::string tt256m1 = +"115792089237316195423570985008687907853269984665640564039457584007913129639935" +; + +const std::string tt255 = +"57896044618658097711785492504343953926634992332820282019728792003956564819968"; + +const std::string tt176 = +"95780971304118053647396689196894323976171195136475136"; + +std::string unsignedToDecimal(unsigned branch); + +std::string decimalAdd(std::string a, std::string b); + +std::string decimalMul(std::string a, std::string b); + +std::string decimalSub(std::string a, std::string b); + +std::string decimalDiv(std::string a, std::string b); + +std::string decimalMod(std::string a, std::string b); + +std::string decimalModExp(std::string b, std::string e, std::string m); + +bool decimalGt(std::string a, std::string b, bool eqAllowed=false); + +unsigned decimalToUnsigned(std::string a); + +#define utd unsignedToDecimal +#define dtu decimalToUnsigned + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/cmdline.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/cmdline.cpp new file mode 100644 index 000000000..fe2560830 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/cmdline.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include "funcs.h" + +int main(int argv, char** argc) { + if (argv == 1) { + std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n"; + return 0; + } + if (argv == 2 && std::string(argc[1]) == "--help" || std::string(argc[1]) == "-h" ) { + std::cout << argc[1] << "\n"; + + std::cout << "serpent command input\n"; + std::cout << "where input -s for from stdin, a file, or interpreted as serpent code if does not exist as file."; + std::cout << "where command: \n"; + std::cout << " parse: Just parses and returns s-expression code.\n"; + std::cout << " rewrite: Parse, use rewrite rules print s-expressions of result.\n"; + std::cout << " compile: Return resulting compiled EVM code in hex.\n"; + std::cout << " assemble: Return result from step before compilation.\n"; + return 0; + } + + std::string flag = ""; + std::string command = argc[1]; + std::string input; + std::string secondInput; + if (std::string(argc[1]) == "-s") { + flag = command.substr(1); + command = argc[2]; + input = ""; + std::string line; + while (std::getline(std::cin, line)) { + input += line + "\n"; + } + secondInput = argv == 3 ? "" : argc[3]; + } + else { + if (argv == 2) { + std::cerr << "Not enough arguments for serpent cmdline\n"; + throw(0); + } + input = argc[2]; + secondInput = argv == 3 ? "" : argc[3]; + } + bool haveSec = secondInput.length() > 0; + if (command == "parse" || command == "parse_serpent") { + std::cout << printAST(parseSerpent(input), haveSec) << "\n"; + } + else if (command == "rewrite") { + std::cout << printAST(rewrite(parseLLL(input, true)), haveSec) << "\n"; + } + else if (command == "compile_to_lll") { + std::cout << printAST(compileToLLL(input), haveSec) << "\n"; + } + else if (command == "rewrite_chunk") { + std::cout << printAST(rewriteChunk(parseLLL(input, true)), haveSec) << "\n"; + } + else if (command == "compile_chunk_to_lll") { + std::cout << printAST(compileChunkToLLL(input), haveSec) << "\n"; + } + else if (command == "build_fragtree") { + std::cout << printAST(buildFragmentTree(parseLLL(input, true))) << "\n"; + } + else if (command == "compile_lll") { + std::cout << binToHex(compileLLL(parseLLL(input, true))) << "\n"; + } + else if (command == "dereference") { + std::cout << printAST(dereference(parseLLL(input, true)), haveSec) <<"\n"; + } + else if (command == "pretty_assemble") { + std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n"; + } + else if (command == "pretty_compile_lll") { + std::cout << printTokens(prettyCompileLLL(parseLLL(input, true))) << "\n"; + } + else if (command == "pretty_compile") { + std::cout << printTokens(prettyCompile(input)) << "\n"; + } + else if (command == "pretty_compile_chunk") { + std::cout << printTokens(prettyCompileChunk(input)) << "\n"; + } + else if (command == "assemble") { + std::cout << assemble(parseLLL(input, true)) << "\n"; + } + else if (command == "serialize") { + std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n"; + } + else if (command == "flatten") { + std::cout << printTokens(flatten(parseLLL(input, true))) << "\n"; + } + else if (command == "deserialize") { + std::cout << printTokens(deserialize(hexToBin(input))) << "\n"; + } + else if (command == "compile") { + std::cout << binToHex(compile(input)) << "\n"; + } + else if (command == "compile_chunk") { + std::cout << binToHex(compileChunk(input)) << "\n"; + } + else if (command == "encode_datalist") { + std::vector tokens = tokenize(input); + std::vector o; + for (int i = 0; i < (int)tokens.size(); i++) { + o.push_back(tokens[i].val); + } + std::cout << binToHex(encodeDatalist(o)) << "\n"; + } + else if (command == "decode_datalist") { + std::vector o = decodeDatalist(hexToBin(input)); + std::vector tokens; + for (int i = 0; i < (int)o.size(); i++) + tokens.push_back(token(o[i])); + std::cout << printTokens(tokens) << "\n"; + } + else if (command == "tokenize") { + std::cout << printTokens(tokenize(input)); + } + else if (command == "biject") { + if (argv == 3) + std::cerr << "Not enough arguments for biject\n"; + int pos = decimalToUnsigned(secondInput); + std::vector n = prettyCompile(input); + if (pos >= (int)n.size()) + std::cerr << "Code position too high\n"; + Metadata m = n[pos].metadata; + std::cout << "Opcode: " << n[pos].val << ", file: " << m.file << + ", line: " << m.ln << ", char: " << m.ch << "\n"; + } +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.cpp new file mode 100644 index 000000000..b9281dcbc --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.cpp @@ -0,0 +1,554 @@ +#include +#include +#include +#include +#include "util.h" +#include "bignum.h" +#include "opcodes.h" + +struct programAux { + std::map vars; + int nextVarMem; + bool allocUsed; + bool calldataUsed; + int step; + int labelLength; +}; + +struct programVerticalAux { + int height; + std::string innerScopeName; + std::map dupvars; + std::map funvars; + std::vector scopes; +}; + +struct programData { + programAux aux; + Node code; + int outs; +}; + +programAux Aux() { + programAux o; + o.allocUsed = false; + o.calldataUsed = false; + o.step = 0; + o.nextVarMem = 32; + return o; +} + +programVerticalAux verticalAux() { + programVerticalAux o; + o.height = 0; + o.dupvars = std::map(); + o.funvars = std::map(); + o.scopes = std::vector(); + return o; +} + +programData pd(programAux aux = Aux(), Node code=token("_"), int outs=0) { + programData o; + o.aux = aux; + o.code = code; + o.outs = outs; + return o; +} + +Node multiToken(Node nodes[], int len, Metadata met) { + std::vector out; + for (int i = 0; i < len; i++) { + out.push_back(nodes[i]); + } + return astnode("_", out, met); +} + +Node finalize(programData c); + +Node popwrap(Node node) { + Node nodelist[] = { + node, + token("POP", node.metadata) + }; + return multiToken(nodelist, 2, node.metadata); +} + +// Grabs variables +mss getVariables(Node node, mss cur=mss()) { + Metadata m = node.metadata; + // Tokens don't contain any variables + if (node.type == TOKEN) + return cur; + // Don't descend into call fragments + else if (node.val == "lll") + return getVariables(node.args[1], cur); + // At global scope get/set/ref also declare + else if (node.val == "get" || node.val == "set" || node.val == "ref") { + if (node.args[0].type != TOKEN) + err("Variable name must be simple token," + " not complex expression!", m); + if (!cur.count(node.args[0].val)) { + cur[node.args[0].val] = utd(cur.size() * 32 + 32); + //std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n"; + } + } + // Recursively process children + for (unsigned i = 0; i < node.args.size(); i++) { + cur = getVariables(node.args[i], cur); + } + return cur; +} + +// Turns LLL tree into tree of code fragments +programData opcodeify(Node node, + programAux aux=Aux(), + programVerticalAux vaux=verticalAux()) { + std::string symb = "_"+mkUniqueToken(); + Metadata m = node.metadata; + // Get variables + if (!aux.vars.size()) { + aux.vars = getVariables(node); + aux.nextVarMem = aux.vars.size() * 32 + 32; + } + // Numbers + if (node.type == TOKEN) { + return pd(aux, nodeToNumeric(node), 1); + } + else if (node.val == "ref" || node.val == "get" || node.val == "set") { + std::string varname = node.args[0].val; + // Determine reference to variable + Node varNode = tkn(aux.vars[varname], m); + //std::cerr << varname << " " << printSimple(varNode) << "\n"; + // Set variable + if (node.val == "set") { + programData sub = opcodeify(node.args[1], aux, vaux); + if (!sub.outs) + err("Value to set variable must have nonzero arity!", m); + // What if we are setting a stack variable? + if (vaux.dupvars.count(node.args[0].val)) { + int h = vaux.height - vaux.dupvars[node.args[0].val]; + if (h > 16) err("Too deep for stack variable (max 16)", m); + Node nodelist[] = { + sub.code, + token("SWAP"+unsignedToDecimal(h), m), + token("POP", m) + }; + return pd(sub.aux, multiToken(nodelist, 3, m), 0); + } + // Setting a memory variable + else { + Node nodelist[] = { + sub.code, + varNode, + token("MSTORE", m), + }; + return pd(sub.aux, multiToken(nodelist, 3, m), 0); + } + } + // Get variable + else if (node.val == "get") { + // Getting a stack variable + if (vaux.dupvars.count(node.args[0].val)) { + int h = vaux.height - vaux.dupvars[node.args[0].val]; + if (h > 16) err("Too deep for stack variable (max 16)", m); + return pd(aux, token("DUP"+unsignedToDecimal(h)), 1); + } + // Getting a memory variable + else { + Node nodelist[] = + { varNode, token("MLOAD", m) }; + return pd(aux, multiToken(nodelist, 2, m), 1); + } + } + // Refer variable + else if (node.val == "ref") { + if (vaux.dupvars.count(node.args[0].val)) + err("Cannot ref stack variable!", m); + return pd(aux, varNode, 1); + } + } + // Comments do nothing + else if (node.val == "comment") { + Node nodelist[] = { }; + return pd(aux, multiToken(nodelist, 0, m), 0); + } + // Custom operation sequence + // eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc + if (node.val == "ops") { + std::vector subs2; + int depth = 0; + for (unsigned i = 0; i < node.args.size(); i++) { + std::string op = upperCase(node.args[i].val); + if (node.args[i].type == ASTNODE || opinputs(op) == -1) { + programVerticalAux vaux2 = vaux; + vaux2.height = vaux.height - i - 1 + node.args.size(); + programData sub = opcodeify(node.args[i], aux, vaux2); + aux = sub.aux; + depth += sub.outs; + subs2.push_back(sub.code); + } + else { + subs2.push_back(token(op, m)); + depth += opoutputs(op) - opinputs(op); + } + } + if (depth < 0 || depth > 1) err("Stack depth mismatch", m); + return pd(aux, astnode("_", subs2, m), 0); + } + // Code blocks + if (node.val == "lll" && node.args.size() == 2) { + if (node.args[1].val != "0") aux.allocUsed = true; + std::vector o; + o.push_back(finalize(opcodeify(node.args[0]))); + programData sub = opcodeify(node.args[1], aux, vaux); + Node code = astnode("____CODE", o, m); + Node nodelist[] = { + token("$begincode"+symb+".endcode"+symb, m), token("DUP1", m), + token("$begincode"+symb, m), sub.code, token("CODECOPY", m), + token("$endcode"+symb, m), token("JUMP", m), + token("~begincode"+symb, m), code, + token("~endcode"+symb, m), token("JUMPDEST", m) + }; + return pd(sub.aux, multiToken(nodelist, 11, m), 1); + } + // Stack variables + if (node.val == "with") { + programData initial = opcodeify(node.args[1], aux, vaux); + programVerticalAux vaux2 = vaux; + vaux2.dupvars[node.args[0].val] = vaux.height; + vaux2.height += 1; + if (!initial.outs) + err("Initial variable value must have nonzero arity!", m); + programData sub = opcodeify(node.args[2], initial.aux, vaux2); + Node nodelist[] = { + initial.code, + sub.code + }; + programData o = pd(sub.aux, multiToken(nodelist, 2, m), sub.outs); + if (sub.outs) + o.code.args.push_back(token("SWAP1", m)); + o.code.args.push_back(token("POP", m)); + return o; + } + // Seq of multiple statements + if (node.val == "seq") { + std::vector children; + int lastOut = 0; + for (unsigned i = 0; i < node.args.size(); i++) { + programData sub = opcodeify(node.args[i], aux, vaux); + aux = sub.aux; + if (sub.outs == 1) { + if (i < node.args.size() - 1) sub.code = popwrap(sub.code); + else lastOut = 1; + } + children.push_back(sub.code); + } + return pd(aux, astnode("_", children, m), lastOut); + } + // 2-part conditional (if gets rewritten to unless in rewrites) + else if (node.val == "unless" && node.args.size() == 2) { + programData cond = opcodeify(node.args[0], aux, vaux); + programData action = opcodeify(node.args[1], cond.aux, vaux); + aux = action.aux; + if (!cond.outs) err("Condition of if/unless statement has arity 0", m); + if (action.outs) action.code = popwrap(action.code); + Node nodelist[] = { + cond.code, + token("$endif"+symb, m), token("JUMPI", m), + action.code, + token("~endif"+symb, m), token("JUMPDEST", m) + }; + return pd(aux, multiToken(nodelist, 6, m), 0); + } + // 3-part conditional + else if (node.val == "if" && node.args.size() == 3) { + programData ifd = opcodeify(node.args[0], aux, vaux); + programData thend = opcodeify(node.args[1], ifd.aux, vaux); + programData elsed = opcodeify(node.args[2], thend.aux, vaux); + aux = elsed.aux; + if (!ifd.outs) + err("Condition of if/unless statement has arity 0", m); + // Handle cases where one conditional outputs something + // and the other does not + int outs = (thend.outs && elsed.outs) ? 1 : 0; + if (thend.outs > outs) thend.code = popwrap(thend.code); + if (elsed.outs > outs) elsed.code = popwrap(elsed.code); + Node nodelist[] = { + ifd.code, + token("ISZERO", m), + token("$else"+symb, m), token("JUMPI", m), + thend.code, + token("$endif"+symb, m), token("JUMP", m), + token("~else"+symb, m), token("JUMPDEST", m), + elsed.code, + token("~endif"+symb, m), token("JUMPDEST", m) + }; + return pd(aux, multiToken(nodelist, 12, m), outs); + } + // While (rewritten to this in rewrites) + else if (node.val == "until") { + programData cond = opcodeify(node.args[0], aux, vaux); + programData action = opcodeify(node.args[1], cond.aux, vaux); + aux = action.aux; + if (!cond.outs) + err("Condition of while/until loop has arity 0", m); + if (action.outs) action.code = popwrap(action.code); + Node nodelist[] = { + token("~beg"+symb, m), token("JUMPDEST", m), + cond.code, + token("$end"+symb, m), token("JUMPI", m), + action.code, + token("$beg"+symb, m), token("JUMP", m), + token("~end"+symb, m), token("JUMPDEST", m), + }; + return pd(aux, multiToken(nodelist, 10, m)); + } + // Memory allocations + else if (node.val == "alloc") { + programData bytez = opcodeify(node.args[0], aux, vaux); + aux = bytez.aux; + if (!bytez.outs) + err("Alloc input has arity 0", m); + aux.allocUsed = true; + Node nodelist[] = { + bytez.code, + token("MSIZE", m), token("SWAP1", m), token("MSIZE", m), + token("ADD", m), + token("0", m), token("SWAP1", m), token("MSTORE", m) + }; + return pd(aux, multiToken(nodelist, 8, m), 1); + } + // All other functions/operators + else { + std::vector subs2; + int depth = opinputs(upperCase(node.val)); + if (depth == -1) + err("Not a function or opcode: "+node.val, m); + if ((int)node.args.size() != depth) + err("Invalid arity for "+node.val, m); + for (int i = node.args.size() - 1; i >= 0; i--) { + programVerticalAux vaux2 = vaux; + vaux2.height = vaux.height - i - 1 + node.args.size(); + programData sub = opcodeify(node.args[i], aux, vaux2); + aux = sub.aux; + if (!sub.outs) + err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata); + subs2.push_back(sub.code); + } + subs2.push_back(token(upperCase(node.val), m)); + int outdepth = opoutputs(upperCase(node.val)); + return pd(aux, astnode("_", subs2, m), outdepth); + } +} + +// Adds necessary wrappers to a program +Node finalize(programData c) { + std::vector bottom; + Metadata m = c.code.metadata; + // If we are using both alloc and variables, we need to pre-zfill + // some memory + if ((c.aux.allocUsed || c.aux.calldataUsed) && c.aux.vars.size() > 0) { + Node nodelist[] = { + token("0", m), + token(unsignedToDecimal(c.aux.nextVarMem - 1)), + token("MSTORE8", m) + }; + bottom.push_back(multiToken(nodelist, 3, m)); + } + // The actual code + bottom.push_back(c.code); + return astnode("_", bottom, m); +} + +//LLL -> code fragment tree +Node buildFragmentTree(Node node) { + return finalize(opcodeify(node)); +} + + +// Builds a dictionary mapping labels to variable names +programAux buildDict(Node program, programAux aux, int labelLength) { + Metadata m = program.metadata; + // Token + if (program.type == TOKEN) { + if (isNumberLike(program)) { + aux.step += 1 + toByteArr(program.val, m).size(); + } + else if (program.val[0] == '~') { + aux.vars[program.val.substr(1)] = unsignedToDecimal(aux.step); + } + else if (program.val[0] == '$') { + aux.step += labelLength + 1; + } + else aux.step += 1; + } + // A sub-program (ie. LLL) + else if (program.val == "____CODE") { + programAux auks = Aux(); + for (unsigned i = 0; i < program.args.size(); i++) { + auks = buildDict(program.args[i], auks, labelLength); + } + for (std::map::iterator it=auks.vars.begin(); + it != auks.vars.end(); + it++) { + aux.vars[(*it).first] = (*it).second; + } + aux.step += auks.step; + } + // Normal sub-block + else { + for (unsigned i = 0; i < program.args.size(); i++) { + aux = buildDict(program.args[i], aux, labelLength); + } + } + return aux; +} + +// Applies that dictionary +Node substDict(Node program, programAux aux, int labelLength) { + Metadata m = program.metadata; + std::vector out; + std::vector inner; + if (program.type == TOKEN) { + if (program.val[0] == '$') { + std::string tokStr = "PUSH"+unsignedToDecimal(labelLength); + out.push_back(token(tokStr, m)); + int dotLoc = program.val.find('.'); + if (dotLoc == -1) { + std::string val = aux.vars[program.val.substr(1)]; + inner = toByteArr(val, m, labelLength); + } + else { + std::string start = aux.vars[program.val.substr(1, dotLoc-1)], + end = aux.vars[program.val.substr(dotLoc + 1)], + dist = decimalSub(end, start); + inner = toByteArr(dist, m, labelLength); + } + out.push_back(astnode("_", inner, m)); + } + else if (program.val[0] == '~') { } + else if (isNumberLike(program)) { + inner = toByteArr(program.val, m); + out.push_back(token("PUSH"+unsignedToDecimal(inner.size()))); + out.push_back(astnode("_", inner, m)); + } + else return program; + } + else { + for (unsigned i = 0; i < program.args.size(); i++) { + Node n = substDict(program.args[i], aux, labelLength); + if (n.type == TOKEN || n.args.size()) out.push_back(n); + } + } + return astnode("_", out, m); +} + +// Compiled fragtree -> compiled fragtree without labels +Node dereference(Node program) { + int sz = treeSize(program) * 4; + int labelLength = 1; + while (sz >= 256) { labelLength += 1; sz /= 256; } + programAux aux = buildDict(program, Aux(), labelLength); + return substDict(program, aux, labelLength); +} + +// Dereferenced fragtree -> opcodes +std::vector flatten(Node derefed) { + std::vector o; + if (derefed.type == TOKEN) { + o.push_back(derefed); + } + else { + for (unsigned i = 0; i < derefed.args.size(); i++) { + std::vector oprime = flatten(derefed.args[i]); + for (unsigned j = 0; j < oprime.size(); j++) o.push_back(oprime[j]); + } + } + return o; +} + +// Opcodes -> bin +std::string serialize(std::vector codons) { + std::string o; + for (unsigned i = 0; i < codons.size(); i++) { + int v; + if (isNumberLike(codons[i])) { + v = decimalToUnsigned(codons[i].val); + } + else if (codons[i].val.substr(0,4) == "PUSH") { + v = 95 + decimalToUnsigned(codons[i].val.substr(4)); + } + else { + v = opcode(codons[i].val); + } + o += (char)v; + } + return o; +} + +// Bin -> opcodes +std::vector deserialize(std::string ser) { + std::vector o; + int backCount = 0; + for (unsigned i = 0; i < ser.length(); i++) { + unsigned char v = (unsigned char)ser[i]; + std::string oper = op((int)v); + if (oper != "" && backCount <= 0) o.push_back(token(oper)); + else if (v >= 96 && v < 128 && backCount <= 0) { + o.push_back(token("PUSH"+unsignedToDecimal(v - 95))); + } + else o.push_back(token(unsignedToDecimal(v))); + if (v >= 96 && v < 128 && backCount <= 0) { + backCount = v - 95; + } + else backCount--; + } + return o; +} + +// Fragtree -> bin +std::string assemble(Node fragTree) { + return serialize(flatten(dereference(fragTree))); +} + +// Fragtree -> tokens +std::vector prettyAssemble(Node fragTree) { + return flatten(dereference(fragTree)); +} + +// LLL -> bin +std::string compileLLL(Node program) { + return assemble(buildFragmentTree(program)); +} + +// LLL -> tokens +std::vector prettyCompileLLL(Node program) { + return prettyAssemble(buildFragmentTree(program)); +} + +// Converts a list of integer values to binary transaction data +std::string encodeDatalist(std::vector vals) { + std::string o; + for (unsigned i = 0; i < vals.size(); i++) { + std::vector n = toByteArr(strToNumeric(vals[i]), Metadata(), 32); + for (unsigned j = 0; j < n.size(); j++) { + int v = decimalToUnsigned(n[j].val); + o += (char)v; + } + } + return o; +} + +// Converts binary transaction data into a list of integer values +std::vector decodeDatalist(std::string ser) { + std::vector out; + for (unsigned i = 0; i < ser.length(); i+= 32) { + std::string o = "0"; + for (unsigned j = i; j < i + 32; j++) { + int vj = (int)(unsigned char)ser[j]; + o = decimalAdd(decimalMul(o, "256"), unsignedToDecimal(vj)); + } + out.push_back(o); + } + return out; +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.h new file mode 100644 index 000000000..aecaa3718 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/compiler.h @@ -0,0 +1,43 @@ +#ifndef ETHSERP_COMPILER +#define ETHSERP_COMPILER + +#include +#include +#include +#include +#include "util.h" + +// Compiled fragtree -> compiled fragtree without labels +Node dereference(Node program); + +// LLL -> fragtree +Node buildFragmentTree(Node program); + +// Dereferenced fragtree -> opcodes +std::vector flatten(Node derefed); + +// opcodes -> bin +std::string serialize(std::vector codons); + +// Fragtree -> bin +std::string assemble(Node fragTree); + +// Fragtree -> opcodes +std::vector prettyAssemble(Node fragTree); + +// LLL -> bin +std::string compileLLL(Node program); + +// LLL -> opcodes +std::vector prettyCompileLLL(Node program); + +// bin -> opcodes +std::vector deserialize(std::string ser); + +// Converts a list of integer values to binary transaction data +std::string encodeDatalist(std::vector vals); + +// Converts binary transaction data into a list of integer values +std::vector decodeDatalist(std::string ser); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/example.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/example.cpp new file mode 100644 index 000000000..1ce2590d0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/example.cpp @@ -0,0 +1,11 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << printAST(compileToLLL(get_file_contents("examples/namecoin.se"))) << "\n"; + cout << decimalSub("10234", "10234") << "\n"; + cout << decimalSub("10234", "10233") << "\n"; +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/collatz.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/collatz.se new file mode 100644 index 000000000..148b47b59 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/collatz.se @@ -0,0 +1,11 @@ +x = msg.data[0] +steps = 0 + +while x > 1: + steps += 1 + if (x % 2) == 0: + x /= 2 + else: + x = 3 * x + 1 + +return(steps) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/counterparty.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/counterparty.se new file mode 100644 index 000000000..abec0d102 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/counterparty.se @@ -0,0 +1,274 @@ +# Ethereum forks Counterparty in 340 lines of serpent +# Not yet tested + +# assets[i] = a registered asset, assets[i].holders[j] = former or current i-holder +data assets[2^50](creator, name, calldate, callprice, dividend_paid, holders[2^50], holdersCount) +data nextAssetId + +# holdersMap: holdersMap[addr][asset] = 1 if addr holds asset +data holdersMap[2^160][2^50] + +# balances[x][y] = how much of y x holds +data balances[2^160][2^50] + +# orders[a][b] = heap of indices to (c, d, e) +# = c offers to sell d units of a at a price of e units of b per 10^18 units +# of a +data orderbooks[2^50][2^50] + +# store of general order data +data orders[2^50](seller, asset_sold, quantity, price) +data ordersCount + +# data feeds +data feeds[2^50](owner, value) +data feedCount + +# heap +data heap +extern heap: [register, push, pop, top, size] + +data cfds[2^50](maker, acceptor, feed, asset, strike, leverage, min, max, maturity) +data cfdCount + +data bets[2^50](maker, acceptor, feed, asset, makerstake, acceptorstake, eqtest, maturity) +data betCount + +def init(): + heap = create('heap.se') + +# Add units (internal method) +def add(to, asset, value): + assert msg.sender == self + self.balances[to][asset] += value + # Add the holder to the holders list + if not self.holdersMap[to][asset]: + self.holdersMap[to][asset] = 1 + c = self.assets[asset].holdersCount + self.assets[asset].holders[c] = to + self.assets[asset].holdersCount = c + 1 + +# Register a new asset +def register_asset(q, name, calldate, callprice): + newid = self.nextAssetId + self.assets[newid].creator = msg.sender + self.assets[newid].name = name + self.assets[newid].calldate = calldate + self.assets[newid].callprice = callprice + self.assets[newid].holders[0] = msg.sender + self.assets[newid].holdersCount = 1 + self.balances[msg.sender][newid] = q + self.holdersMap[msg.sender][newid] = 1 + +# Send +def send(to, asset, value): + fromval = self.balances[msg.sender][asset] + if fromval >= value: + self.balances[msg.sender][asset] -= value + self.add(to, asset, value) + +# Order +def mkorder(selling, buying, quantity, price): + # Make sure you have enough to pay for the order + assert self.balances[msg.sender][selling] >= quantity: + # Try to match existing orders + o = orderbooks[buying][selling] + if not o: + o = self.heap.register() + orderbooks[selling][buying] = o + sz = self.heap.size(o) + invprice = 10^36 / price + while quantity > 0 and sz > 0: + orderid = self.heap.pop() + p = self.orders[orderid].price + if p > invprice: + sz = 0 + else: + q = self.orders[orderid].quantity + oq = min(q, quantity) + b = self.orders[orderid].seller + self.balances[msg.sender][selling] -= oq * p / 10^18 + self.add(msg.sender, buying, oq) + self.add(b, selling, oq * p / 10^18) + self.orders[orderid].quantity = q - oq + if oq == q: + self.orders[orderid].seller = 0 + self.orders[orderid].price = 0 + self.orders[orderid].asset_sold = 0 + quantity -= oq + sz -= 1 + assert quantity > 0 + # Make the order + c = self.ordersCount + self.orders[c].seller = msg.sender + self.orders[c].asset_sold = selling + self.orders[c].quantity = quantity + self.orders[c].price = price + self.ordersCount += 1 + # Add it to the heap + o = orderbooks[selling][buying] + if not o: + o = self.heap.register() + orderbooks[selling][buying] = o + self.balances[msg.sender][selling] -= quantity + self.heap.push(o, price, c) + return(c) + +def cancel_order(id): + if self.orders[id].seller == msg.sender: + self.orders[id].seller = 0 + self.orders[id].price = 0 + self.balances[msg.sender][self.orders[id].asset_sold] += self.orders[id].quantity + self.orders[id].quantity = 0 + self.orders[id].asset_sold = 0 + +def register_feed(): + c = self.feedCount + self.feeds[c].owner = msg.sender + self.feedCount = c + 1 + return(c) + +def set_feed(id, v): + if self.feeds[id].owner == msg.sender: + self.feeds[id].value = v + +def mk_cfd_offer(feed, asset, strike, leverage, min, max, maturity): + b = self.balances[msg.sender][asset] + req = max((strike - min) * leverage, (strike - max) * leverage) + assert b >= req + self.balances[msg.sender][asset] = b - req + c = self.cfdCount + self.cfds[c].maker = msg.sender + self.cfds[c].feed = feed + self.cfds[c].asset = asset + self.cfds[c].strike = strike + self.cfds[c].leverage = leverage + self.cfds[c].min = min + self.cfds[c].max = max + self.cfds[c].maturity = maturity + self.cfdCount = c + 1 + return(c) + +def accept_cfd_offer(c): + assert not self.cfds[c].acceptor and self.cfds[c].maker + asset = self.cfds[c].asset + strike = self.cfds[c].strike + min = self.cfds[c].min + max = self.cfds[c].max + leverage = self.cfds[c].leverage + b = self.balances[msg.sender][asset] + req = max((min - strike) * leverage, (max - strike) * leverage) + assert b >= req + self.balances[msg.sender][asset] = b - req + self.cfds[c].acceptor = msg.sender + self.cfds[c].maturity += block.timestamp + +def claim_cfd_offer(c): + asset = self.cfds[c].asset + strike = self.cfds[c].strike + min = self.cfds[c].min + max = self.cfds[c].max + leverage = self.cfds[c].leverage + v = self.feeds[self.cfds[c].feed].value + assert v <= min or v >= max or block.timestamp >= self.cfds[c].maturity + maker_req = max((strike - min) * leverage, (strike - max) * leverage) + acceptor_req = max((min - strike) * leverage, (max - strike) * leverage) + paydelta = (strike - v) * leverage + self.add(self.cfds[c].maker, asset, maker_req + paydelta) + self.add(self.cfds[c].acceptor, asset, acceptor_req - paydelta) + self.cfds[c].maker = 0 + self.cfds[c].acceptor = 0 + self.cfds[c].feed = 0 + self.cfds[c].asset = 0 + self.cfds[c].strike = 0 + self.cfds[c].leverage = 0 + self.cfds[c].min = 0 + self.cfds[c].max = 0 + self.cfds[c].maturity = 0 + +def withdraw_cfd_offer(c): + if self.cfds[c].maker == msg.sender and not self.cfds[c].acceptor: + asset = self.cfds[c].asset + strike = self.cfds[c].strike + min = self.cfds[c].min + max = self.cfds[c].max + leverage = self.cfds[c].leverage + maker_req = max((strike - min) * leverage, (strike - max) * leverage) + self.balances[self.cfds[c].maker][asset] += maker_req + self.cfds[c].maker = 0 + self.cfds[c].acceptor = 0 + self.cfds[c].feed = 0 + self.cfds[c].asset = 0 + self.cfds[c].strike = 0 + self.cfds[c].leverage = 0 + self.cfds[c].min = 0 + self.cfds[c].max = 0 + self.cfds[c].maturity = 0 + + +def mk_bet_offer(feed, asset, makerstake, acceptorstake, eqtest, maturity): + assert self.balances[msg.sender][asset] >= makerstake + c = self.betCount + self.bets[c].maker = msg.sender + self.bets[c].feed = feed + self.bets[c].asset = asset + self.bets[c].makerstake = makerstake + self.bets[c].acceptorstake = acceptorstake + self.bets[c].eqtest = eqtest + self.bets[c].maturity = maturity + self.balances[msg.sender][asset] -= makerstake + self.betCount = c + 1 + return(c) + +def accept_bet_offer(c): + assert self.bets[c].maker and not self.bets[c].acceptor + asset = self.bets[c].asset + acceptorstake = self.bets[c].acceptorstake + assert self.balances[msg.sender][asset] >= acceptorstake + self.balances[msg.sender][asset] -= acceptorstake + self.bets[c].acceptor = msg.sender + +def claim_bet_offer(c): + assert block.timestamp >= self.bets[c].maturity + v = self.feeds[self.bets[c].feed].value + totalstake = self.bets[c].makerstake + self.bets[c].acceptorstake + if v == self.bets[c].eqtest: + self.add(self.bets[c].maker, self.bets[c].asset, totalstake) + else: + self.add(self.bets[c].acceptor, self.bets[c].asset, totalstake) + self.bets[c].maker = 0 + self.bets[c].feed = 0 + self.bets[c].asset = 0 + self.bets[c].makerstake = 0 + self.bets[c].acceptorstake = 0 + self.bets[c].eqtest = 0 + self.bets[c].maturity = 0 + +def cancel_bet(c): + assert not self.bets[c].acceptor and msg.sender == self.bets[c].maker + self.balances[msg.sender][self.bets[c].asset] += self.bets[c].makerstake + self.bets[c].maker = 0 + self.bets[c].feed = 0 + self.bets[c].asset = 0 + self.bets[c].makerstake = 0 + self.bets[c].acceptorstake = 0 + self.bets[c].eqtest = 0 + self.bets[c].maturity = 0 + +def dividend(holder_asset, divvying_asset, ratio): + i = 0 + sz = self.assets[holder_asset].holdersCount + t = 0 + holders = array(sz) + payments = array(sz) + while i < sz: + holders[i] = self.assets[holder_asset].holders[i] + payments[i] = self.balances[holders[i]][holder_asset] * ratio / 10^18 + t += payments[i] + i += 1 + if self.balances[msg.sender][divvying_asset] >= t: + i = 0 + while i < sz: + self.add(holders[i], divvying_asset, payments[i]) + i += 1 + self.balances[msg.sender][divvying_asset] -= t diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/heap.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/heap.se new file mode 100644 index 000000000..4a43a3974 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/counterparty/heap.se @@ -0,0 +1,69 @@ +data heaps[2^50](owner, size, nodes[2^50](key, value)) +data heapIndex + +def register(): + i = self.heapIndex + self.heaps[i].owner = msg.sender + self.heapIndex = i + 1 + return(i) + +def push(heap, key, value): + assert msg.sender == self.heaps[heap].owner + sz = self.heaps[heap].size + self.heaps[heap].nodes[sz].key = key + self.heaps[heap].nodes[sz].value = value + k = sz + 1 + while k > 1: + bottom = self.heaps[heap].nodes[k].key + top = self.heaps[heap].nodes[k/2].key + if bottom < top: + tvalue = self.heaps[heap].nodes[k/2].value + bvalue = self.heaps[heap].nodes[k].value + self.heaps[heap].nodes[k].key = top + self.heaps[heap].nodes[k].value = tvalue + self.heaps[heap].nodes[k/2].key = bottom + self.heaps[heap].nodes[k/2].value = bvalue + k /= 2 + else: + k = 0 + self.heaps[heap].size = sz + 1 + +def pop(heap): + sz = self.heaps[heap].size + assert sz + prevtop = self.heaps[heap].nodes[1].value + self.heaps[heap].nodes[1].key = self.heaps[heap].nodes[sz].key + self.heaps[heap].nodes[1].value = self.heaps[heap].nodes[sz].value + self.heaps[heap].nodes[sz].key = 0 + self.heaps[heap].nodes[sz].value = 0 + top = self.heaps[heap].nodes[1].key + k = 1 + while k * 2 < sz: + bottom1 = self.heaps[heap].nodes[k * 2].key + bottom2 = self.heaps[heap].nodes[k * 2 + 1].key + if bottom1 < top and (bottom1 < bottom2 or k * 2 + 1 >= sz): + tvalue = self.heaps[heap].nodes[1].value + bvalue = self.heaps[heap].nodes[k * 2].value + self.heaps[heap].nodes[k].key = bottom1 + self.heaps[heap].nodes[k].value = bvalue + self.heaps[heap].nodes[k * 2].key = top + self.heaps[heap].nodes[k * 2].value = tvalue + k = k * 2 + elif bottom2 < top and bottom2 < bottom1 and k * 2 + 1 < sz: + tvalue = self.heaps[heap].nodes[1].value + bvalue = self.heaps[heap].nodes[k * 2 + 1].value + self.heaps[heap].nodes[k].key = bottom2 + self.heaps[heap].nodes[k].value = bvalue + self.heaps[heap].nodes[k * 2 + 1].key = top + self.heaps[heap].nodes[k * 2 + 1].value = tvalue + k = k * 2 + 1 + else: + k = sz + self.heaps[heap].size = sz - 1 + return(prevtop) + +def top(heap): + return(self.heaps[heap].nodes[1].value) + +def size(heap): + return(self.heaps[heap].size) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/crowdfund.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/crowdfund.se new file mode 100644 index 000000000..9fd1e0643 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/crowdfund.se @@ -0,0 +1,53 @@ +data campaigns[2^80](recipient, goal, deadline, contrib_total, contrib_count, contribs[2^50](sender, value)) + +def create_campaign(id, recipient, goal, timelimit): + if self.campaigns[id].recipient: + return(0) + self.campaigns[id].recipient = recipient + self.campaigns[id].goal = goal + self.campaigns[id].deadline = block.timestamp + timelimit + +def contribute(id): + # Update contribution total + total_contributed = self.campaigns[id].contrib_total + msg.value + self.campaigns[id].contrib_total = total_contributed + + # Record new contribution + sub_index = self.campaigns[id].contrib_count + self.campaigns[id].contribs[sub_index].sender = msg.sender + self.campaigns[id].contribs[sub_index].value = msg.value + self.campaigns[id].contrib_count = sub_index + 1 + + # Enough funding? + if total_contributed >= self.campaigns[id].goal: + send(self.campaigns[id].recipient, total_contributed) + self.clear(id) + return(1) + + # Expired? + if block.timestamp > self.campaigns[id].deadline: + i = 0 + c = self.campaigns[id].contrib_count + while i < c: + send(self.campaigns[id].contribs[i].sender, self.campaigns[id].contribs[i].value) + i += 1 + self.clear(id) + return(2) + +def progress_report(id): + return(self.campaigns[id].contrib_total) + +# Clearing function for internal use +def clear(id): + if self == msg.sender: + self.campaigns[id].recipient = 0 + self.campaigns[id].goal = 0 + self.campaigns[id].deadline = 0 + c = self.campaigns[id].contrib_count + self.campaigns[id].contrib_count = 0 + self.campaigns[id].contrib_total = 0 + i = 0 + while i < c: + self.campaigns[id].contribs[i].sender = 0 + self.campaigns[id].contribs[i].value = 0 + i += 1 diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/futarchy.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/futarchy.se new file mode 100644 index 000000000..0d68622ac --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/futarchy.se @@ -0,0 +1,136 @@ +# 0: current epoch +# 1: number of proposals +# 2: master currency +# 3: last winning market +# 4: last txid +# 5: long-term ema currency units purchased +# 6: last block when currency units purchased +# 7: ether allocated to last round +# 8: last block when currency units claimed +# 9: ether allocated to current round +# 1000+: [proposal address, market ID, totprice, totvolume] + +init: + # We technically have two levels of epoch here. We have + # one epoch of 1000, to synchronize with the 1000 epoch + # of the market, and then 100 of those epochs make a + # meta-epoch (I'll nominate the term "seculum") over + # which the futarchy protocol will take place + contract.storage[0] = block.number / 1000 + # The master currency of the futarchy. The futarchy will + # assign currency units to whoever the prediction market + # thinks will best increase the currency's value + master_currency = create('subcurrency.se') + contract.storage[2] = master_currency +code: + curepoch = block.number / 1000 + prevepoch = contract.storage[0] + if curepoch > prevepoch: + if (curepoch % 100) > 50: + # Collect price data + # We take an average over 50 subepochs to determine + # the price of each asset, weighting by volume to + # prevent abuse + contract.storage[0] = curepoch + i = 0 + numprop = contract.storage[1] + while i < numprop: + market = contract.storage[1001 + i * 4] + price = call(market, 2) + volume = call(market, 3) + contract.storage[1002 + i * 4] += price + contract.storage[1003 + i * 4] += volume * price + i += 1 + if (curepoch / 100) > (prevepoch / 100): + # If we are entering a new seculum, we determine the + # market with the highest total average price + best = 0 + bestmarket = 0 + besti = 0 + i = 0 + while i < numprop: + curtotprice = contract.storage[1002 + i * 4] + curvolume = contract.storage[1002 + i * 4] + curavgprice = curtotprice / curvolume + if curavgprice > best: + best = curavgprice + besti = i + bestmarket = contract.storage[1003 + i * 4] + i += 1 + # Reset the number of proposals to 0 + contract.storage[1] = 0 + # Reward the highest proposal + call(contract.storage[2], [best, 10^9, 0], 3) + # Record the winning market so we can later appropriately + # compensate the participants + contract.storage[2] = bestmarket + # The amount of ether allocated to the last round + contract.storage[7] = contract.storage[9] + # The amount of ether allocated to the next round + contract.storage[9] = contract.balance / 2 + # Make a proposal [0, address] + if msg.data[0] == 0 and curepoch % 100 < 50: + pid = contract.storage[1] + market = create('market.se') + c1 = create('subcurrency.se') + c2 = create('subcurrency.se') + call(market, [c1, c2], 2) + contract.storage[1000 + pid * 4] = msg.data[1] + contract.storage[1001 + pid * 4] = market + contract.storage[1] += 1 + # Claim ether [1, address] + # One unit of the first currency in the last round's winning + # market entitles you to a quantity of ether that was decided + # at the start of that epoch + elif msg.data[0] == 1: + first_subcurrency = call(contract.storage[2], 3) + # We ask the first subcurrency contract what the last transaction was. The + # way to make a claim is to send the amount of first currency units that + # you wish to claim with, and then immediately call this contract. For security + # it makes sense to set up a tx which sends both messages in sequence atomically + data = call(first_subcurrency, [], 0, 4) + from = data[0] + to = data[1] + value = data[2] + txid = data[3] + if txid > contract.storage[4] and to == contract.address: + send(to, contract.storage[7] * value / 10^9) + contract.storage[4] = txid + # Claim second currency [2, address] + # One unit of the second currency in the last round's winning + # market entitles you to one unit of the futarchy's master + # currency + elif msg.data[0] == 2: + second_subcurrency = call(contract.storage[2], 3) + data = call(first_subcurrency, [], 0, 4) + from = data[0] + to = data[1] + value = data[2] + txid = data[3] + if txid > contract.storage[4] and to == contract.address: + call(contract.storage[2], [to, value], 2) + contract.storage[4] = txid + # Purchase currency for ether (target releasing 10^9 units per seculum) + # Price starts off 1 eth for 10^9 units but increases hyperbolically to + # limit issuance + elif msg.data[0] == 3: + pre_ema = contract.storage[5] + post_ema = pre_ema + msg.value + pre_reserve = 10^18 / (10^9 + pre_ema / 10^9) + post_reserve = 10^18 / (10^9 + post_ema / 10^9) + call(contract.storage[2], [msg.sender, pre_reserve - post_reserve], 2) + last_sold = contract.storage[6] + contract.storage[5] = pre_ema * (100000 + last_sold - block.number) + msg.value + contract.storage[6] = block.number + # Claim all currencies as the ether miner of the current block + elif msg.data[0] == 2 and msg.sender == block.coinbase and block.number > contract.storage[8]: + i = 0 + numproposals = contract.storage[1] + while i < numproposals: + market = contract.storage[1001 + i * 3] + fc = call(market, 4) + sc = call(market, 5) + call(fc, [msg.sender, 1000], 2) + call(sc, [msg.sender, 1000], 2) + i += 1 + contract.storage[8] = block.number diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/heap.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/heap.se new file mode 100644 index 000000000..1bc442e6d --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/heap.se @@ -0,0 +1,55 @@ +# 0: size +# 1-n: elements + +init: + contract.storage[1000] = msg.sender +code: + # Only owner of the heap is allowed to modify it + if contract.storage[1000] != msg.sender: + stop + # push + if msg.data[0] == 0: + sz = contract.storage[0] + contract.storage[sz + 1] = msg.data[1] + k = sz + 1 + while k > 1: + bottom = contract.storage[k] + top = contract.storage[k/2] + if bottom < top: + contract.storage[k] = top + contract.storage[k/2] = bottom + k /= 2 + else: + k = 0 + contract.storage[0] = sz + 1 + # pop + elif msg.data[0] == 1: + sz = contract.storage[0] + if !sz: + return(0) + prevtop = contract.storage[1] + contract.storage[1] = contract.storage[sz] + contract.storage[sz] = 0 + top = contract.storage[1] + k = 1 + while k * 2 < sz: + bottom1 = contract.storage[k * 2] + bottom2 = contract.storage[k * 2 + 1] + if bottom1 < top and (bottom1 < bottom2 or k * 2 + 1 >= sz): + contract.storage[k] = bottom1 + contract.storage[k * 2] = top + k = k * 2 + elif bottom2 < top and bottom2 < bottom1 and k * 2 + 1 < sz: + contract.storage[k] = bottom2 + contract.storage[k * 2 + 1] = top + k = k * 2 + 1 + else: + k = sz + contract.storage[0] = sz - 1 + return(prevtop) + # top + elif msg.data[0] == 2: + return(contract.storage[1]) + # size + elif msg.data[0] == 3: + return(contract.storage[0]) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/market.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/market.se new file mode 100644 index 000000000..2303a0b60 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/market.se @@ -0,0 +1,117 @@ +# Creates a decentralized market between any two subcurrencies + +# Here, the first subcurrency is the base asset and the second +# subcurrency is the asset priced against the base asset. Hence, +# "buying" refers to trading the first for the second, and +# "selling" refers to trading the second for the first + +# storage 0: buy orders +# storage 1: sell orders +# storage 1000: first subcurrency +# storage 1001: last first subcurrency txid +# storage 2000: second subcurrency +# storage 2001: last second subcurrency txid +# storage 3000: current epoch +# storage 4000: price +# storage 4001: volume + +init: + # Heap for buy orders + contract.storage[0] = create('heap.se') + # Heap for sell orders + contract.storage[1] = create('heap.se') +code: + # Initialize with [ first_subcurrency, second_subcurrency ] + if !contract.storage[1000]: + contract.storage[1000] = msg.data[0] # First subcurrency + contract.storage[1001] = -1 + contract.storage[2000] = msg.data[1] # Second subcurrency + contract.storage[2001] = -1 + contract.storage[3000] = block.number / 1000 + stop + first_subcurrency = contract.storage[1000] + second_subcurrency = contract.storage[2000] + buy_heap = contract.storage[0] + sell_heap = contract.storage[1] + # This contract operates in "epochs" of 100 blocks + # At the end of each epoch, we process all orders + # simultaneously, independent of order. This algorithm + # prevents front-running, and generates a profit from + # the spread. The profit is permanently kept in the + # market (ie. destroyed), making both subcurrencies + # more valuable + + # Epoch transition code + if contract.storage[3000] < block.number / 100: + done = 0 + volume = 0 + while !done: + # Grab the top buy and sell order from each heap + topbuy = call(buy_heap, 1) + topsell = call(sell_heap, 1) + # An order is recorded in the heap as: + # Buys: (2^48 - 1 - price) * 2^208 + units of first currency * 2^160 + from + # Sells: price * 2^208 + units of second currency * 2^160 + from + buyprice = -(topbuy / 2^208) + buyfcvalue = (topbuy / 2^160) % 2^48 + buyer = topbuy % 2^160 + sellprice = topsell / 2^208 + sellscvalue = (topsell / 2^160) % 2^48 + seller = topsell % 2^160 + # Heap empty, or no more matching orders + if not topbuy or not topsell or buyprice < sellprice: + done = 1 + else: + # Add to volume counter + volume += buyfcvalue + # Calculate how much of the second currency the buyer gets, and + # how much of the first currency the seller gets + sellfcvalue = sellscvalue / buyprice + buyscvalue = buyfcvalue * sellprice + # Send the currency units along + call(second_subcurrency, [buyer, buyscvalue], 2) + call(first_subcurrency, [seller, sellfcvalue], 2) + if volume: + contract.storage[4000] = (buyprice + sellprice) / 2 + contract.storage[4001] = volume + contract.storage[3000] = block.number / 100 + # Make buy order [0, price] + if msg.data[0] == 0: + # We ask the first subcurrency contract what the last transaction was. The + # way to make a buy order is to send the amount of first currency units that + # you wish to buy with, and then immediately call this contract. For security + # it makes sense to set up a tx which sends both messages in sequence atomically + data = call(first_subcurrency, [], 0, 4) + from = data[0] + to = data[1] + value = data[2] + txid = data[3] + price = msg.data[1] + if txid > contract.storage[1001] and to == contract.address: + contract.storage[1001] = txid + # Adds the order to the heap + call(buy_heap, [0, -price * 2^208 + (value % 2^48) * 2^160 + from], 2) + # Make sell order [1, price] + elif msg.data[0] == 1: + # Same mechanics as buying + data = call(second_subcurrency, [], 0, 4) + from = data[0] + to = data[1] + value = data[2] + txid = data[3] + price = msg.data[1] + if txid > contract.storage[2001] and to == contract.address: + contract.storage[2001] = txid + call(sell_heap, [0, price * 2^208 + (value % 2^48) * 2^160 + from], 2) + # Ask for price + elif msg.data[0] == 2: + return(contract.storage[4000]) + # Ask for volume + elif msg.data[0] == 3: + return(contract.storage[1000]) + # Ask for first currency + elif msg.data[0] == 4: + return(contract.storage[2000]) + # Ask for second currency + elif msg.data[0] == 5: + return(contract.storage[4001]) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/subcurrency.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/subcurrency.se new file mode 100644 index 000000000..1501beff7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/subcurrency.se @@ -0,0 +1,35 @@ +# Initialization +# Admin can issue and delete at will +init: + contract.storage[0] = msg.sender +code: + # If a message with one item is sent, that's a balance query + if msg.datasize == 1: + addr = msg.data[0] + return(contract.storage[addr]) + # If a message with two items [to, value] are sent, that's a transfer request + elif msg.datasize == 2: + from = msg.sender + fromvalue = contract.storage[from] + to = msg.data[0] + value = msg.data[1] + if fromvalue >= value and value > 0 and to > 4: + contract.storage[from] = fromvalue - value + contract.storage[to] += value + contract.storage[2] = from + contract.storage[3] = to + contract.storage[4] = value + contract.storage[5] += 1 + return(1) + return(0) + elif msg.datasize == 3 and msg.sender == contract.storage[0]: + # Admin can issue at will by sending a [to, value, 0] message + if msg.data[2] == 0: + contract.storage[msg.data[0]] += msg.data[1] + # Change admin [ newadmin, 0, 1 ] + # Set admin to 0 to disable administration + elif msg.data[2] == 1: + contract.storage[0] = msg.data[0] + # Fetch last transaction + else: + return([contract.storage[2], contract.storage[3], contract.storage[4], contract.storage[5]], 4) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/test.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/test.py new file mode 100644 index 000000000..301a4a845 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/cyberdyne/test.py @@ -0,0 +1,39 @@ +from __future__ import print_function +import pyethereum +t = pyethereum.tester +s = t.state() +# Create currencies +c1 = s.contract('subcurrency.se') +print("First currency: %s" % c1) +c2 = s.contract('subcurrency.se') +print("First currency: %s" % c2) +# Allocate units +s.send(t.k0, c1, 0, [t.a0, 1000, 0]) +s.send(t.k0, c1, 0, [t.a1, 1000, 0]) +s.send(t.k0, c2, 0, [t.a2, 1000000, 0]) +s.send(t.k0, c2, 0, [t.a3, 1000000, 0]) +print("Allocated units") +# Market +m = s.contract('market.se') +s.send(t.k0, m, 0, [c1, c2]) +# Place orders +s.send(t.k0, c1, 0, [m, 1000]) +s.send(t.k0, m, 0, [0, 1200]) +s.send(t.k1, c1, 0, [m, 1000]) +s.send(t.k1, m, 0, [0, 1400]) +s.send(t.k2, c2, 0, [m, 1000000]) +s.send(t.k2, m, 0, [1, 800]) +s.send(t.k3, c2, 0, [m, 1000000]) +s.send(t.k3, m, 0, [1, 600]) +print("Orders placed") +# Next epoch and ping +s.mine(100) +print("Mined 100") +s.send(t.k0, m, 0, []) +print("Updating") +# Check +assert s.send(t.k0, c2, 0, [t.a0]) == [800000] +assert s.send(t.k0, c2, 0, [t.a1]) == [600000] +assert s.send(t.k0, c1, 0, [t.a2]) == [833] +assert s.send(t.k0, c1, 0, [t.a3]) == [714] +print("Balance checks passed") diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/datafeed.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/datafeed.se new file mode 100644 index 000000000..4c4a56de8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/datafeed.se @@ -0,0 +1,12 @@ +# Database updateable only by the original creator +data creator + +def init(): + self.creator = msg.sender + +def update(k, v): + if msg.sender == self.creator: + self.storage[k] = v + +def query(k): + return(self.storage[k]) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover.se new file mode 100644 index 000000000..ce28f58c2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover.se @@ -0,0 +1,40 @@ +# So I looked up on Wikipedia what Jacobian form actually is, and noticed that it's +# actually a rather different and more clever construction than the naive version +# that I created. It may possible to achieve a further 20-50% savings by applying +# that version. + +extern all: [call] + +data JORDANMUL +data JORDANADD +data EXP + +def init(): + self.JORDANMUL = create('jacobian_mul.se') + self.JORDANADD = create('jacobian_add.se') + self.EXP = create('modexp.se') + +def call(h, v, r, s): + N = -432420386565659656852420866394968145599 + P = -4294968273 + h = mod(h, N) + r = mod(r, P) + s = mod(s, N) + Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 + Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 + x = r + xcubed = mulmod(mulmod(x, x, P), x, P) + beta = self.EXP.call(addmod(xcubed, 7, P), div(P + 1, 4), P) + + # Static-gascost ghetto conditional + y_is_positive = mod(v, 2) xor mod(beta, 2) + y = beta * y_is_positive + (P - beta) * (1 - y_is_positive) + + GZ = self.JORDANMUL.call(Gx, 1, Gy, 1, N - h, outsz=4) + XY = self.JORDANMUL.call(x, 1, y, 1, s, outsz=4) + COMB = self.JORDANADD.call(GZ[0], GZ[1], GZ[2], GZ[3], XY[0], XY[1], XY[2], XY[3], 1, outsz=5) + COMB[4] = self.EXP.call(r, N - 2, N) + Q = self.JORDANMUL.call(data=COMB, datasz=5, outsz=4) + ox = mulmod(Q[0], self.EXP.call(Q[1], P - 2, P), P) + oy = mulmod(Q[2], self.EXP.call(Q[3], P - 2, P), P) + return([ox, oy], 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover_compiled.evm b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover_compiled.evm new file mode 100644 index 000000000..f575fe70f --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/ecrecover_compiled.evm @@ -0,0 +1 @@ +6000607f535961071c80610013593961072f566000605f535961013d8061001359396101505661012b8061000e60003961013956600061023f5360003560001a6000141561012a5760806001602037602051151561002c576060511561002f565b60005b156100695760806080599059016000905260a052600060a051526001602060a05101526000604060a05101526001606060a051015260a051f25b6401000003d160000380608051826003846020516020510909098182600260605109836040516040510909828283098382830984858660026020510983098603866040518509088560405183098686888985604051098a038a85602051090809878689846040510909888960605183098a038a6080518509088960805183096080608059905901600090526101e052866101e051528560206101e05101528260406101e05101528160606101e05101526101e051f250505050505050505050505b5b6000f25b816000f090506000555961040680610168593961056e566000603f535961013d8061001359396101505661012b8061000e60003961013956600061023f5360003560001a6000141561012a5760806001602037602051151561002c576060511561002f565b60005b156100695760806080599059016000905260a052600060a051526001602060a05101526000604060a05101526001606060a051015260a051f25b6401000003d160000380608051826003846020516020510909098182600260605109836040516040510909828283098382830984858660026020510983098603866040518509088560405183098686888985604051098a038a85602051090809878689846040510909888960605183098a038a6080518509088960805183096080608059905901600090526101e052866101e051528560206101e05101528260406101e05101528160606101e05101526101e051f250505050505050505050505b5b6000f25b816000f0905060005561029a8061016860003961040256600061043f5360003560001a60001415610299576101006001602037602051151561002d5760605115610030565b60005b1561007657608059905901600090526101405260a051610140515260c051602061014051015260e051604061014051015261010051606061014051015261014051610120525b60a05115156100885760e0511561008b565b60005b156100d0576080599059016000905261016052602051610160515260405160206101605101526060516040610160510152608051606061016051015261016051610120525b61012051156100e157608061012051f25b6401000003d16000036000818260a0516040510983038360c051602051090814156101b1576000818260e051608051098303836101005160605109081415610175576080608080599059016000905260006101c0601f01536020516101e052604051610200526060516102205260805161024052818160816101c0601f01600060005460195a03f1508090509050f26101b0565b608060805990590160009052610280526000610280515260016020610280510152600060406102805101526001606061028051015261028051f25b5b808160405160c051098283610100516060510984038460805160e05109080981828360c0516020510984038460405160a051090883608051610100510909828283098382830984856020518309860386604051850908856040518309868760a051830988038860c0518509088760c051830988898a60405185098b038b8460205109088909898a836040510989098a8b60605183098c038c6080518509088b60805183096080608059905901600090526103e052866103e051528560206103e05101528260406103e05101528160606103e05101526103e051f2505050505050505050505050505b5b6000f25b816000f090506001556101928061058660003961071856600061013f5360003560001a600014156101915760a0600160203770014551231950b75fc4402da1732fc9bebf60000360a0510660a05260a05115606051156020511502011561007e5760806080599059016000905260c052600060c051526001602060c05101526000604060c05101526001606060c051015260c051f25b610120599059016000905260e052600060e051526000602060e05101526001604060e05101526000606060e05101526001608060e0510152600060a060e0510152600060c060e0510152600060e060e0510152600061010060e051015260e0517f80000000000000000000000000000000000000000000000000000000000000005b6000811115610187578060a0511615610165576080602083016081601f85016000600054614e20f15060205160a083015260405160c083015260605160e0830152608051610100830152608060208301610101601f85016000600154614e20f161017b565b6080602083016081601f85016000600054614e20f15b50600281049050610100565b608060208301f250505b5b6000f25b816000f0905060005559610406806107475939610b4d566000603f535961013d8061001359396101505661012b8061000e60003961013956600061023f5360003560001a6000141561012a5760806001602037602051151561002c576060511561002f565b60005b156100695760806080599059016000905260a052600060a051526001602060a05101526000604060a05101526001606060a051015260a051f25b6401000003d160000380608051826003846020516020510909098182600260605109836040516040510909828283098382830984858660026020510983098603866040518509088560405183098686888985604051098a038a85602051090809878689846040510909888960605183098a038a6080518509088960805183096080608059905901600090526101e052866101e051528560206101e05101528260406101e05101528160606101e05101526101e051f250505050505050505050505b5b6000f25b816000f0905060005561029a8061016860003961040256600061043f5360003560001a60001415610299576101006001602037602051151561002d5760605115610030565b60005b1561007657608059905901600090526101405260a051610140515260c051602061014051015260e051604061014051015261010051606061014051015261014051610120525b60a05115156100885760e0511561008b565b60005b156100d0576080599059016000905261016052602051610160515260405160206101605101526060516040610160510152608051606061016051015261016051610120525b61012051156100e157608061012051f25b6401000003d16000036000818260a0516040510983038360c051602051090814156101b1576000818260e051608051098303836101005160605109081415610175576080608080599059016000905260006101c0601f01536020516101e052604051610200526060516102205260805161024052818160816101c0601f01600060005460195a03f1508090509050f26101b0565b608060805990590160009052610280526000610280515260016020610280510152600060406102805101526001606061028051015261028051f25b5b808160405160c051098283610100516060510984038460805160e05109080981828360c0516020510984038460405160a051090883608051610100510909828283098382830984856020518309860386604051850908856040518309868760a051830988038860c0518509088760c051830988898a60405185098b038b8460205109088909898a836040510989098a8b60605183098c038c6080518509088b60805183096080608059905901600090526103e052866103e051528560206103e05101528260406103e05101528160606103e05101526103e051f2505050505050505050505050505b5b6000f25b816000f09050600155596100d080610b655939610c35566100be8061000e6000396100cc5660003560001a600014156100bd576060600160203760017f80000000000000000000000000000000000000000000000000000000000000005b60008111156100b157606051816040511615156020510a606051848509099150606051600282046040511615156020510a606051848509099150606051600482046040511615156020510a606051848509099150606051600882046040511615156020510a606051848509099150601081049050610038565b8160c052602060c0f250505b5b6000f25b816000f090506002556103d280610c4d60003961101f56600061095f5360003560001a600014156103d1576080600160203770014551231950b75fc4402da1732fc9bebf60000360a0526401000003d160000360c05260a0516020510660205260c0516060510660605260a051608051066080527f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179860e0527f483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8610100526060516101205260c0516101205160c05161012051610120510909610140526000610180601f015360c051600761014051086101a0526004600160c05101046101c05260c0516101e05260206102006061610180601f01600060025460195a03f1506102005161016052600261016051066002604051061861022052610220516001036101605160c05103026102205161016051020161024052608080599059016000905260006102a0601f015360e0516102c05260016102e052610100516103005260016103205260205160a0510361034052818160a16102a0601f01600060005460195a03f150809050905061026052608080599059016000905260006103c0601f0153610120516103e052600161040052610240516104205260016104405260805161046052818160a16103c0601f01600060005460195a03f15080905090506103805260a080599059016000905260006104e0601f015361026051516105005260206102605101516105205260406102605101516105405260606102605101516105605261038051516105805260206103805101516105a05260406103805101516105c05260606103805101516105e05260016106005281816101216104e0601f01600060015460195a03f15080905090506104a0526000610640601f015360605161066052600260a051036106805260a0516106a05260206106c06061610640601f01600060025460195a03f1506106c05160806104a05101526104a05160208103805160018303608080599059016000905260008353818160a185600060005460195a03f150838552809050905090509050905090506106e05260c05160006107e0601f015360206106e051015161080052600260c051036108205260c05161084052602061086060616107e0601f01600060025460195a03f150610860516106e05151096107c05260c05160006108a0601f015360606106e05101516108c052600260c051036108e05260c05161090052602061092060616108a0601f01600060025460195a03f1506109205160406106e05101510961088052604060405990590160009052610940526107c051610940515261088051602061094051015261094051f25b5b6000f2 diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_add.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_add.se new file mode 100644 index 000000000..29dc390b2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_add.se @@ -0,0 +1,32 @@ +extern all: [call] +data DOUBLE + +def init(): + self.DOUBLE = create('jacobian_double.se') + +def call(axn, axd, ayn, ayd, bxn, bxd, byn, byd): + if !axn and !ayn: + o = [bxn, bxd, byn, byd] + if !bxn and !byn: + o = [axn, axd, ayn, ayd] + if o: + return(o, 4) + with P = -4294968273: + if addmod(mulmod(axn, bxd, P), P - mulmod(axd, bxn, P), P) == 0: + if addmod(mulmod(ayn, byd, P), P - mulmod(ayd, byn, P), P) == 0: + return(self.DOUBLE.call(axn, axd, ayn, ayd, outsz=4), 4) + else: + return([0, 1, 0, 1], 4) + with mn = mulmod(addmod(mulmod(byn, ayd, P), P - mulmod(ayn, byd, P), P), mulmod(bxd, axd, P), P): + with md = mulmod(mulmod(byd, ayd, P), addmod(mulmod(bxn, axd, P), P - mulmod(axn, bxd, P), P), P): + with msqn = mulmod(mn, mn, P): + with msqd = mulmod(md, md, P): + with msqman = addmod(mulmod(msqn, axd, P), P - mulmod(msqd, axn, P), P): + with msqmad = mulmod(msqd, axd, P): + with xn = addmod(mulmod(msqman, bxd, P), P - mulmod(msqmad, bxn, P), P): + with xd = mulmod(msqmad, bxd, P): + with mamxn = mulmod(mn, addmod(mulmod(axn, xd, P), P - mulmod(xn, axd, P), P), P): + with mamxd = mulmod(md, mulmod(axd, xd, P), P): + with yn = addmod(mulmod(mamxn, ayd, P), P - mulmod(mamxd, ayn, P), P): + with yd = mulmod(mamxd, ayd, P): + return([xn, xd, yn, yd], 4) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_double.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_double.se new file mode 100644 index 000000000..b7d8221a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_double.se @@ -0,0 +1,16 @@ +def call(axn, axd, ayn, ayd): + if !axn and !ayn: + return([0, 1, 0, 1], 4) + with P = -4294968273: + # No need to add (A, 1) because A = 0 for bitcoin + with mn = mulmod(mulmod(mulmod(axn, axn, P), 3, P), ayd, P): + with md = mulmod(mulmod(axd, axd, P), mulmod(ayn, 2, P), P): + with msqn = mulmod(mn, mn, P): + with msqd = mulmod(md, md, P): + with xn = addmod(mulmod(msqn, axd, P), P - mulmod(msqd, mulmod(axn, 2, P), P), P): + with xd = mulmod(msqd, axd, P): + with mamxn = mulmod(addmod(mulmod(axn, xd, P), P - mulmod(axd, xn, P), P), mn, P): + with mamxd = mulmod(mulmod(axd, xd, P), md, P): + with yn = addmod(mulmod(mamxn, ayd, P), P - mulmod(mamxd, ayn, P), P): + with yd = mulmod(mamxd, ayd, P): + return([xn, xd, yn, yd], 4) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_mul.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_mul.se new file mode 100644 index 000000000..bf5b96bb4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/jacobian_mul.se @@ -0,0 +1,37 @@ +# Expected gas cost +# +# def expect(n, point_at_infinity=False): +# n = n % (2**256 - 432420386565659656852420866394968145599) +# if point_at_infinity: +# return 79 +# if n == 0: +# return 34479 +# L = int(1 + math.log(n) / math.log(2)) +# H = len([x for x in b.encode(n, 2) if x == '1']) +# return 34221 + 94 * L + 343 * H + +data DOUBLE +data ADD + +def init(): + self.DOUBLE = create('jacobian_double.se') + self.ADD = create('jacobian_add.se') + +def call(axn, axd, ayn, ayd, n): + n = mod(n, -432420386565659656852420866394968145599) + if !axn * !ayn + !n: # Constant-gas version of !axn and !ayn or !n + return([0, 1, 0, 1], 4) + with o = [0, 0, 1, 0, 1, 0, 0, 0, 0]: + with b = 2 ^ 255: + while gt(b, 0): + if n & b: + ~call(20000, self.DOUBLE, 0, o + 31, 129, o + 32, 128) + o[5] = axn + o[6] = axd + o[7] = ayn + o[8] = ayd + ~call(20000, self.ADD, 0, o + 31, 257, o + 32, 128) + else: + ~call(20000, self.DOUBLE, 0, o + 31, 129, o + 32, 128) + b = div(b, 2) + return(o + 32, 4) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/modexp.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/modexp.se new file mode 100644 index 000000000..687b12a04 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/modexp.se @@ -0,0 +1,11 @@ +def call(b, e, m): + with o = 1: + with bit = 2 ^ 255: + while gt(bit, 0): + # A touch of loop unrolling for 20% efficiency gain + o = mulmod(mulmod(o, o, m), b ^ !(!(e & bit)), m) + o = mulmod(mulmod(o, o, m), b ^ !(!(e & div(bit, 2))), m) + o = mulmod(mulmod(o, o, m), b ^ !(!(e & div(bit, 4))), m) + o = mulmod(mulmod(o, o, m), b ^ !(!(e & div(bit, 8))), m) + bit = div(bit, 16) + return(o) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/substitutes.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/substitutes.py new file mode 100644 index 000000000..0007da0cf --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/substitutes.py @@ -0,0 +1,78 @@ +import bitcoin as b +import math +import sys + + +def signed(o): + return map(lambda x: x - 2**256 if x >= 2**255 else x, o) + + +def hamming_weight(n): + return len([x for x in b.encode(n, 2) if x == '1']) + + +def binary_length(n): + return len(b.encode(n, 2)) + + +def jacobian_mul_substitute(A, B, C, D, N): + if A == 0 and C == 0 or (N % b.N) == 0: + return {"gas": 86, "output": [0, 1, 0, 1]} + else: + output = b.jordan_multiply(((A, B), (C, D)), N) + return { + "gas": 35262 + 95 * binary_length(N % b.N) + 355 * hamming_weight(N % b.N), + "output": signed(list(output[0]) + list(output[1])) + } + + +def jacobian_add_substitute(A, B, C, D, E, F, G, H): + if A == 0 or E == 0: + gas = 149 + elif (A * F - B * E) % b.P == 0: + if (C * H - D * G) % b.P == 0: + gas = 442 + else: + gas = 177 + else: + gas = 301 + output = b.jordan_add(((A, B), (C, D)), ((E, F), (G, H))) + return { + "gas": gas, + "output": signed(list(output[0]) + list(output[1])) + } + + +def modexp_substitute(base, exp, mod): + return { + "gas": 5150, + "output": signed([pow(base, exp, mod) if mod > 0 else 0]) + } + + +def ecrecover_substitute(z, v, r, s): + P, A, B, N, Gx, Gy = b.P, b.A, b.B, b.N, b.Gx, b.Gy + x = r + beta = pow(x*x*x+A*x+B, (P + 1) / 4, P) + BETA_PREMIUM = modexp_substitute(x, (P + 1) / 4, P)["gas"] + y = beta if v % 2 ^ beta % 2 else (P - beta) + Gz = b.jordan_multiply(((Gx, 1), (Gy, 1)), (N - z) % N) + GZ_PREMIUM = jacobian_mul_substitute(Gx, 1, Gy, 1, (N - z) % N)["gas"] + XY = b.jordan_multiply(((x, 1), (y, 1)), s) + XY_PREMIUM = jacobian_mul_substitute(x, 1, y, 1, s % N)["gas"] + Qr = b.jordan_add(Gz, XY) + QR_PREMIUM = jacobian_add_substitute(Gz[0][0], Gz[0][1], Gz[1][0], Gz[1][1], + XY[0][0], XY[0][1], XY[1][0], XY[1][1] + )["gas"] + Q = b.jordan_multiply(Qr, pow(r, N - 2, N)) + Q_PREMIUM = jacobian_mul_substitute(Qr[0][0], Qr[0][1], Qr[1][0], Qr[1][1], + pow(r, N - 2, N))["gas"] + R_PREMIUM = modexp_substitute(r, N - 2, N)["gas"] + OX_PREMIUM = modexp_substitute(Q[0][1], P - 2, P)["gas"] + OY_PREMIUM = modexp_substitute(Q[1][1], P - 2, P)["gas"] + Q = b.from_jordan(Q) + return { + "gas": 991 + BETA_PREMIUM + GZ_PREMIUM + XY_PREMIUM + QR_PREMIUM + + Q_PREMIUM + R_PREMIUM + OX_PREMIUM + OY_PREMIUM, + "output": signed(Q) + } diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/test.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/test.py new file mode 100644 index 000000000..48d21e32f --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/ecc/test.py @@ -0,0 +1,129 @@ +import bitcoin as b +import random +import sys +import math +from pyethereum import tester as t +import substitutes +import time + +vals = [random.randrange(2**256) for i in range(12)] + +test_points = [list(p[0]) + list(p[1]) for p in + [b.jordan_multiply(((b.Gx, 1), (b.Gy, 1)), r) for r in vals]] + +G = [b.Gx, 1, b.Gy, 1] +Z = [0, 1, 0, 1] + + +def neg_point(p): + return [p[0], b.P - p[1], p[2], b.P - p[3]] + +s = t.state() +s.block.gas_limit = 10000000 +t.gas_limit = 1000000 + + +c = s.contract('modexp.se') +print "Starting modexp tests" + +for i in range(0, len(vals) - 2, 3): + o1 = substitutes.modexp_substitute(vals[i], vals[i+1], vals[i+2]) + o2 = s.profile(t.k0, c, 0, funid=0, abi=vals[i:i+3]) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + +c = s.contract('jacobian_add.se') +print "Starting addition tests" + +for i in range(2): + P = test_points[i * 2] + Q = test_points[i * 2 + 1] + NP = neg_point(P) + + o1 = substitutes.jacobian_add_substitute(*(P + Q)) + o2 = s.profile(t.k0, c, 0, funid=0, abi=P + Q) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + + o1 = substitutes.jacobian_add_substitute(*(P + NP)) + o2 = s.profile(t.k0, c, 0, funid=0, abi=P + NP) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + + o1 = substitutes.jacobian_add_substitute(*(P + P)) + o2 = s.profile(t.k0, c, 0, funid=0, abi=P + P) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + + o1 = substitutes.jacobian_add_substitute(*(P + Z)) + o2 = s.profile(t.k0, c, 0, funid=0, abi=P + Z) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + + o1 = substitutes.jacobian_add_substitute(*(Z + P)) + o2 = s.profile(t.k0, c, 0, funid=0, abi=Z + P) + #assert o1["gas"] == o2["gas"], (o1, o2) + assert o1["output"] == o2["output"], (o1, o2) + + +c = s.contract('jacobian_mul.se') +print "Starting multiplication tests" + + +mul_tests = [ + Z + [0], + Z + [vals[0]], + test_points[0] + [0], + test_points[1] + [b.N], + test_points[2] + [1], + test_points[2] + [2], + test_points[2] + [3], + test_points[2] + [4], + test_points[3] + [5], + test_points[3] + [6], + test_points[4] + [7], + test_points[4] + [2**254], + test_points[4] + [vals[1]], + test_points[4] + [vals[2]], + test_points[4] + [vals[3]], + test_points[5] + [2**256 - 1], +] + +for i, test in enumerate(mul_tests): + print 'trying mul_test %i' % i, test + o1 = substitutes.jacobian_mul_substitute(*test) + o2 = s.profile(t.k0, c, 0, funid=0, abi=test) + # assert o1["gas"] == o2["gas"], (o1, o2, test) + assert o1["output"] == o2["output"], (o1, o2, test) + +c = s.contract('ecrecover.se') +print "Starting ecrecover tests" + +for i in range(5): + print 'trying ecrecover_test', vals[i*2], vals[i*2+1] + k = vals[i*2] + h = vals[i*2+1] + V, R, S = b.ecdsa_raw_sign(b.encode(h, 256, 32), k) + aa = time.time() + o1 = substitutes.ecrecover_substitute(h, V, R, S) + print 'sub', time.time() - aa + a = time.time() + o2 = s.profile(t.k0, c, 0, funid=0, abi=[h, V, R, S]) + print time.time() - a + # assert o1["gas"] == o2["gas"], (o1, o2, h, V, R, S) + assert o1["output"] == o2["output"], (o1, o2, h, V, R, S) + +# Explicit tests + +data = [[ + 0xf007a9c78a4b2213220adaaf50c89a49d533fbefe09d52bbf9b0da55b0b90b60, + 0x1b, + 0x5228fc9e2fabfe470c32f459f4dc17ef6a0a81026e57e4d61abc3bc268fc92b5, + 0x697d4221cd7bc5943b482173de95d3114b9f54c5f37cc7f02c6910c6dd8bd107 +]] + +for datum in data: + o1 = substitutes.ecrecover_substitute(*datum) + o2 = s.profile(t.k0, c, 0, funid=0, abi=datum) + #assert o1["gas"] == o2["gas"], (o1, o2, datum) + assert o1["output"] == o2["output"], (o1, o2, datum) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/channel.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/channel.se new file mode 100644 index 000000000..733f4a95b --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/channel.se @@ -0,0 +1,45 @@ +if msg.data[0] == 0: + new_id = contract.storage[-1] + # store [from, to, value, maxvalue, timeout] in contract storage + contract.storage[new_id] = msg.sender + contract.storage[new_id + 1] = msg.data[1] + contract.storage[new_id + 2] = 0 + contract.storage[new_id + 3] = msg.value + contract.storage[new_id + 4] = 2^254 + # increment next id + contract.storage[-1] = new_id + 10 + # return id of this channel + return(new_id) + +# Increase payment on channel: [1, id, value, v, r, s] +elif msg.data[0] == 1: + # Ecrecover native extension; will be a different address in testnet and live + ecrecover = 0x46a8d0b21b1336d83b06829f568d7450df36883f + # Message data parameters + id = msg.data[1] % 2^160 + value = msg.data[2] + # Determine sender from signature + h = sha3([id, value], 2) + sender = call(ecrecover, [h, msg.data[3], msg.data[4], msg.data[5]], 4) + # Check sender matches and new value is greater than old + if sender == contract.storage[id]: + if value > contract.storage[id + 2] and value <= contract.storage[id + 3]: + # Update channel, increasing value and setting timeout + contract.storage[id + 2] = value + contract.storage[id + 4] = block.number + 1000 + +# Cash out channel: [2, id] +elif msg.data[0] == 2: + id = msg.data[1] % 2^160 + # Check if timeout has run out + if block.number >= contract.storage[id + 3]: + # Send funds + send(contract.storage[id + 1], contract.storage[id + 2]) + # Send refund + send(contract.storage[id], contract.storage[id + 3] - contract.storage[id + 2]) + # Clear storage + contract.storage[id] = 0 + contract.storage[id + 1] = 0 + contract.storage[id + 2] = 0 + contract.storage[id + 3] = 0 + contract.storage[id + 4] = 0 diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/map.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/map.se new file mode 100644 index 000000000..768dfb9fc --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/map.se @@ -0,0 +1,19 @@ +# An implementation of a contract for storing a key/value binding +init: + # Set owner + contract.storage[0] = msg.sender +code: + # Check ownership + if msg.sender == contract.storage[0]: + # Get: returns (found, val) + if msg.data[0] == 0: + s = sha3(msg.data[1]) + return([contract.storage[s], contract.storage[s+1]], 2) + # Set: sets map[k] = v + elif msg.data[0] == 1: + s = sha3(msg.data[1]) + contract.storage[s] = 1 + contract.storage[s + 1] = msg.data[2] + # Suicide + elif msg.data[2] == 1: + suicide(0) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/multiforward.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/multiforward.se new file mode 100644 index 000000000..577794d97 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/multiforward.se @@ -0,0 +1,14 @@ +init: + contract.storage[0] = msg.sender +code: + if msg.sender != contract.storage[0]: + stop + i = 0 + while i < ~calldatasize(): + to = ~calldataload(i) + value = ~calldataload(i+20) / 256^12 + datasize = ~calldataload(i+32) / 256^30 + data = alloc(datasize) + ~calldatacopy(data, i+34, datasize) + ~call(tx.gas - 25, to, value, data, datasize, 0, 0) + i += 34 + datasize diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/shadowchain.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/shadowchain.se new file mode 100644 index 000000000..1e466a355 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/eth15/shadowchain.se @@ -0,0 +1,166 @@ +# Exists in state: +# (i) last committed block +# (ii) chain of uncommitted blocks (linear only) +# (iii) transactions, each tx with an associated block number +# +# Uncommitted block = +# [ numtxs, numkvs, tx1 (N words), tx2 (N words) ..., [k1, v1], [k2, v2], [k3, v3] ... ] +# +# Block checking process +# +# Suppose last committed state is m +# Last uncommitted state is n +# Contested block is b +# +# 1. Temporarily apply all state transitions from +# m to b +# 2. Run code, get list of changes +# 3. Check is list of changes matches deltas +# * if yes, do nothing +# * if no, set last uncommitted state to pre-b +# +# Storage variables: +# +# Last committed block: 0 +# Last uncommitted block: 1 +# Contract holding code: 2 +# Uncommitted map: 3 +# Transaction length (parameter): 4 +# Block b: 2^160 + b * 2^40: +# + 1: submission blknum +# + 2: submitter +# + 3: data in uncommitted block format above +# Last committed storage: +# sha3(k): index k + +# Initialize: [0, c, txlength], set address of the code-holding contract and the transaction +# length +if not contract.storage[2]: + contract.storage[2] = msg.data[1] + contract.storage[4] = msg.data[2] + stop + +# Sequentially commit all uncommitted blocks that are more than 1000 mainchain-blocks old +last_committed_block = contract.storage[0] +last_uncommitted_block = contract.storage[1] +lcb_storage_index = 2^160 + last_committed_block * 2^40 +while contract.storage[lcb_storage_index + 1] < block.number - 1000 and last_committed_block < last_uncommitted_block: + kvpairs = contract.storage[lcb_storage_index] + i = 0 + while i < kvpairs: + k = contract.storage[lcb_storage_index + 3 + i * 2] + v = contract.storage[lcb_storage_index + 4 + i * 2] + contract.storage[sha3(k)] = v + i += 1 + last_committed_block += 1 + lcb_storage_index += 2^40 +contract.storage[0] = last_committed_block + + +# Propose block: [ 0, block number, data in block format above ... ] +if msg.data[0] == 0: + blknumber = msg.data[1] + # Block number must be correct + if blknumber != contract.storage[1]: + stop + # Deposit requirement + if msg.value < 10^19: + stop + # Store the proposal in storage as + # [ 0, main-chain block number, sender, block data...] + start_index = 2^160 + blknumber * 2^40 + numkvs = (msg.datasize - 2) / 2 + contract.storage[start_index + 1] = block.number + 1ontract.storage[start_index + 2] = msg.sender + i = 0 + while i < msg.datasize - 2: + contract.storage[start_index + 3 + i] = msg.data[2 + i] + i += 1 + contract.storage[1] = blknumber + 1 + +# Challenge block: [ 1, b ] +elif msg.data[0] == 1: + blknumber = msg.data[1] + txwidth = contract.storage[4] + last_uncommitted_block = contract.storage[1] + last_committed_block = contract.storage[0] + # Cannot challenge nonexistent or committed blocks + if blknumber <= last_uncommitted_block or blknumber > last_committed_block: + stop + # Create a contract to serve as a map that maintains keys and values + # temporarily + tempstore = create('map.se') + contract.storage[3] = tempstore + # Unquestioningly apply the state transitions from the last committed block + # up to b + b = last_committed_block + cur_storage_index = 2^160 + last_committed_block * 2^40 + while b < blknumber: + numtxs = contract.storage[cur_storage_index + 3] + numkvs = contract.storage[cur_storage_index + 4] + kv0index = cur_storage_index + 5 + numtxs * txwidth + i = 0 + while i < numkvs: + k = contract.storage[kv0index + i * 2] + v = contract.storage[kx0index + i * 2 + 1] + call(tempstore, [1, k, v], 3) + i += 1 + b += 1 + cur_storage_index += 2^40 + # Run the actual code, and see what state transitions it outputs + # The way that the code is expected to work is to: + # + # (1) take as input the list of transactions (the contract should + # use msg.datasize to determine how many txs there are, and it should + # be aware of the value of txwidth) + # (2) call this contract with [2, k] to read current state data + # (3) call this contract with [3, k, v] to write current state data + # (4) return as output a list of all state transitions that it made + # in the form [kvcount, k1, v1, k2, v2 ... ] + # + # The reason for separating (2) from (3) is that sometimes the state + # transition may end up changing a given key many times, and we don't + # need to inefficiently store that in storage + numkvs = contract.storage[cur_storage_index + 3] + numtxs = contract.storage[cur_storage_index + 4] + # Populate input array + inpwidth = numtxs * txwidth + inp = array(inpwidth) + i = 0 + while i < inpwidth: + inp[i] = contract.storage[cur_storage_index + 5 + i] + i += 1 + out = call(contract.storage[2], inp, inpwidth, numkvs * 2 + 1) + # Check that the number of state transitions is the same + if out[0] != kvcount: + send(msg.sender, 10^19) + contract.storage[0] = last_committed_block + stop + kv0index = cur_storage_index + 5 + numtxs * txwidth + i = 0 + while i < kvcount: + # Check that each individual state transition matches + k = contract.storage[kv0index + i * 2 + 1] + v = contract.storage[kv0index + i * 2 + 2] + if k != out[i * 2 + 1] or v != out[i * 2 + 2]: + send(msg.sender, 10^19) + contract.storage[0] = last_committed_block + stop + i += 1 + # Suicide tempstore + call(tempstore, 2) + + +# Read data [2, k] +elif msg.data[0] == 2: + tempstore = contract.storage[3] + o = call(tempstore, [0, msg.data[1]], 2, 2) + if o[0]: + return(o[1]) + else: + return contract.storage[sha3(msg.data[1])] + +# Write data [3, k, v] +elif msg.data[0] == 3: + tempstore = contract.storage[3] + call(tempstore, [1, msg.data[1], msg.data[2]], 3, 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/fixedpoint.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/fixedpoint.se new file mode 100644 index 000000000..a8073c685 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/fixedpoint.se @@ -0,0 +1,31 @@ +type f: [a, b, c, d, e] + +macro f($a) + f($b): + f(add($a, $b)) + +macro f($a) - f($b): + f(sub($a, $b)) + +macro f($a) * f($b): + f(mul($a, $b) / 10000) + +macro f($a) / f($b): + f(sdiv($a * 10000, $b)) + +macro f($a) % f($b): + f(smod($a, $b)) + +macro f($v) = f($w): + $v = $w + +macro unfify(f($a)): + $a / 10000 + +macro fify($a): + f($a * 10000) + +a = fify(5) +b = fify(2) +c = a / b +e = c + (a / b) +return(unfify(e)) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/long_integer_macros.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/long_integer_macros.se new file mode 100644 index 000000000..58cdce6ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/long_integer_macros.se @@ -0,0 +1,116 @@ +macro smin($a, $b): + with $1 = $a: + with $2 = $b: + if(slt($1, $2), $1, $2) + +macro smax($a, $b): + with $1 = $a: + with $2 = $b: + if(slt($1, $2), $2, $1) + +def omul(x, y): + o = expose(mklong(x) * mklong(y)) + return(slice(o, 1), o[0]+1) + +def oadd(x, y): + o = expose(mklong(x) + mklong(y)) + return(slice(o, 1), o[0]+1) + +def osub(x, y): + o = expose(mklong(x) - mklong(y)) + return(slice(o, 1), o[0]+1) + +def odiv(x, y): + o = expose(mklong(x) / mklong(y)) + return(slice(o, 1), o[0]+1) + +def comb(a:a, b:a, sign): + sz = smax(a[0], b[0]) + msz = smin(a[0], b[0]) + c = array(sz + 2) + c[0] = sz + i = 0 + carry = 0 + while i < msz: + m = a[i + 1] + sign * b[i + 1] + carry + c[i + 1] = mod(m + 2^127, 2^128) - 2^127 + carry = (div(m + 2^127, 2^128) + 2^127) % 2^128 - 2^127 + i += 1 + u = if(a[0] > msz, a, b) + s = if(a[0] > msz, 1, sign) + while i < sz: + m = s * u[i + 1] + carry + c[i + 1] = mod(m + 2^127, 2^128) - 2^127 + carry = (div(m + 2^127, 2^128) + 2^127) % 2^128 - 2^127 + i += 1 + if carry: + c[0] += 1 + c[sz + 1] = carry + return(c, c[0]+1) + +def mul(a:a, b:a): + c = array(a[0] + b[0] + 2) + c[0] = a[0] + b[0] + i = 0 + while i < a[0]: + j = 0 + carry = 0 + while j < b[0]: + m = c[i + j + 1] + a[i + 1] * b[j + 1] + carry + c[i + j + 1] = mod(m + 2^127, 2^128) - 2^127 + carry = (div(m + 2^127, 2^128) + 2^127) % 2^128 - 2^127 + j += 1 + if carry: + c[0] = a[0] + b[0] + 1 + c[i + j + 1] += carry + i += 1 + return(c, c[0]+1) + +macro long($a) + long($b): + long(self.comb($a:$a[0]+1, $b:$b[0]+1, 1, outsz=$a[0]+$b[0]+2)) + +macro long($a) - long($b): + long(self.comb($a:$a[0]+1, $b:$b[0]+1, -1, outsz=$a[0]+$b[0]+2)) + +macro long($a) * long($b): + long(self.mul($a:$a[0]+1, $b:$b[0]+1, outsz=$a[0]+$b[0]+2)) + +macro long($a) / long($b): + long(self.div($a:$a[0]+1, $b:$b[0]+1, outsz=$a[0]+$b[0]+2)) + +macro mulexpand(long($a), $k, $m): + long: + with $c = array($a[0]+k+2): + $c[0] = $a[0]+$k + with i = 0: + while i < $a[0]: + v = $a[i+1] * $m + $c[i+$k+1] + $c[i+$k+1] = mod(v + 2^127, 2^128) - 2^127 + $c[i+$k+2] = div(v + 2^127, 2^128) + i += 1 + $c + +def div(a:a, b:a): + asz = a[0] + bsz = b[0] + while b[bsz] == 0 and bsz > 0: + bsz -= 1 + c = array(asz+2) + c[0] = asz+1 + while 1: + while a[asz] == 0 and asz > 0: + asz -= 1 + if asz < bsz: + return(c, c[0]+1) + sub = expose(mulexpand(long(b), asz - bsz, a[asz] / b[bsz])) + c[asz - bsz+1] = a[asz] / b[bsz] + a = expose(long(a) - long(sub)) + a[asz-1] += 2^128 * a[asz] + a[asz] = 0 + +macro mklong($i): + long([2, mod($i + 2^127, 2^128) - 2^127, div($i + 2^127, 2^128)]) + +macro expose(long($i)): + $i + diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mul2.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mul2.se new file mode 100644 index 000000000..65adff1e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mul2.se @@ -0,0 +1,2 @@ +def double(v): + return(v*2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se new file mode 100644 index 000000000..3efb0edeb --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se @@ -0,0 +1,187 @@ +# mutuala - subcurrency + +# We want to issue a currency that reduces in value as you store it through negative interest. +# That negative interest would be stored in a commons account. It's like the p2p version of a +# capital tax + +# the same things goes for transactions - you pay as you use the currency. However, the more +# you pay, the more you get to say about what the tax is used for + +# each participant can propose a recipient for a payout to be made out of the commons account, +# others can vote on it by awarding it tax_credits. + +# TODO should proposal have expiration timestamp?, after which the tax_credits are refunded +# TODO multiple proposals can take more credits that available in the Commons, how to handle this +# TODO how to handle lost accounts, after which no longer possible to get 2/3 majority + +shared: + COMMONS = 42 + ADMIN = 666 + CAPITAL_TAX_PER_DAY = 7305 # 5% per year + PAYMENT_TAX = 20 # 5% + + ACCOUNT_LIST_OFFSET = 2^160 + ACCOUNT_MAP_OFFSET = 2^161 + PROPOSAL_LIST_OFFSET = 2^162 + PROPOSAL_MAP_OFFSET = 2^163 + +init: + contract.storage[ADMIN] = msg.sender + contract.storage[ACCOUNT_LIST_OFFSET - 1] = 1 + contract.storage[ACCOUNT_LIST_OFFSET] = msg.sender + contract.storage[ACCOUNT_MAP_OFFSET + msg.sender] = 10^12 + contract.storage[ACCOUNT_MAP_OFFSET + msg.sender + 1] = block.timestamp + +# contract.storage[COMMONS] = balance commons + +# contract.storage[ACCOUNT_LIST_OFFSET - 1] = number of accounts +# contract.storage[ACCOUNT_LIST_OFFSET + n] = account n + +# contract.storage[PROPOSAL_LIST_OFFSET - 1] contains the number of proposals +# contract.storage[PROPOSAL_LIST_OFFSET + n] = proposal n + +# per account: +# contract.storage[ACCOUNT_MAP_OFFSET + account] = balance +# contract.storage[ACCOUNT_MAP_OFFSET + account+1] = timestamp_last_transaction +# contract.storage[ACCOUNT_MAP_OFFSET + account+2] = tax_credits + +# per proposal: +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = recipient +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+1] = amount +# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+2] = total vote credits + +code: + if msg.data[0] == "suicide" and msg.sender == contract.storage[ADMIN]: + suicide(msg.sender) + + elif msg.data[0] == "balance": + addr = msg.data[1] + return(contract.storage[ACCOUNT_MAP_OFFSET + addr]) + + elif msg.data[0] == "pay": + from = msg.sender + fromvalue = contract.storage[ACCOUNT_MAP_OFFSET + from] + to = msg.data[1] + if to == 0 or to >= 2^160: + return([0, "invalid address"], 2) + value = msg.data[2] + tax = value / PAYMENT_TAX + + if fromvalue >= value + tax: + contract.storage[ACCOUNT_MAP_OFFSET + from] = fromvalue - (value + tax) + contract.storage[ACCOUNT_MAP_OFFSET + to] += value + # tax + contract.storage[COMMONS] += tax + contract.storage[ACCOUNT_MAP_OFFSET + from + 2] += tax + + # check timestamp field to see if target account exists + if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0: + # register new account + nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1] + contract.storage[ACCOUNT_LIST_OFFSET + nr_accounts] = to + contract.storage[ACCOUNT_LIST_OFFSET - 1] += 1 + contract.storage[ACCOUNT_MAP_OFFSET + to + 1] = block.timestamp + + return(1) + else: + return([0, "insufficient balance"], 2) + + elif msg.data[0] == "hash": + proposal_id = sha3(msg.data[1]) + return(proposal_id) + + elif msg.data[0] == "propose": + from = msg.sender + # check if sender has an account and has tax credits + if contract.storage[ACCOUNT_MAP_OFFSET + from + 2] == 0: + return([0, "sender has no tax credits"], 2) + + proposal_id = sha3(msg.data[1]) + # check if proposal doesn't already exist + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id]: + return([0, "proposal already exists"]) + + to = msg.data[2] + # check if recipient is a valid address and has an account (with timestamp) + if to == 0 or to >= 2^160: + return([0, "invalid address"], 2) + if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0: + return([0, "invalid to account"], 2) + + value = msg.data[3] + # check if there is enough money in the commons account + if value > contract.storage[COMMONS]: + return([0, "not enough credits in commons"], 2) + + # record proposal in list + nr_proposals = contract.storage[PROPOSAL_LIST_OFFSET - 1] + contract.storage[PROPOSAL_LIST_OFFSET + nr_proposals] = proposal_id + contract.storage[PROPOSAL_LIST_OFFSET - 1] += 1 + + # record proposal in map + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = to + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = value + + return(proposal_id) + + elif msg.data[0] == "vote": + from = msg.sender + proposal_id = sha3(msg.data[1]) + value = msg.data[2] + # check if sender has an account and has tax credits + if value < contract.storage[ACCOUNT_MAP_OFFSET + from + 2]: + return([0, "sender doesn't have enough tax credits"], 2) + + # check if proposal exist + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] == 0: + return([0, "proposal doesn't exist"], 2) + + # increase votes + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] += value + # withdraw tax credits + contract.storage[ACCOUNT_MAP_OFFSET + from + 2] -= value + + # did we reach 2/3 threshold? + if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] >= contract.storage[COMMONS] * 2 / 3: + # got majority + to = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] + amount = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] + + # adjust balances + contract.storage[ACCOUNT_MAP_OFFSET + to] += amount + contract.storage[COMMONS] -= amount + + # reset proposal + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = 0 + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = 0 + contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] = 0 + return(1) + + return(proposal_id) + + elif msg.data[0] == "tick": + nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1] + account_idx = 0 + tax_paid = 0 + # process all accounts and see if they have to pay their daily capital tax + while account_idx < nr_accounts: + cur_account = contract.storage[ACCOUNT_LIST_OFFSET + account_idx] + last_timestamp = contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1] + time_diff = block.timestamp - last_timestamp + if time_diff >= 86400: + tax_days = time_diff / 86400 + balance = contract.storage[ACCOUNT_MAP_OFFSET + cur_account] + tax = tax_days * (balance / CAPITAL_TAX_PER_DAY) + if tax > 0: + # charge capital tax, but give tax credits in return + contract.storage[ACCOUNT_MAP_OFFSET + cur_account] -= tax + contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1] += tax_days * 86400 + contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 2] += tax + + contract.storage[COMMONS] += tax + tax_paid += 1 + account_idx += 1 + return(tax_paid) # how many accounts did we charge tax on + + else: + return([0, "unknown command"], 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/namecoin.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/namecoin.se new file mode 100644 index 000000000..11d6274ae --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/namecoin.se @@ -0,0 +1,7 @@ +def register(k, v): + if !self.storage[k]: # Is the key not yet taken? + # Then take it! + self.storage[k] = v + return(1) + else: + return(0) // Otherwise do nothing diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/peano.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/peano.se new file mode 100644 index 000000000..979854444 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/peano.se @@ -0,0 +1,43 @@ +macro padd($x, psuc($y)): + psuc(padd($x, $y)) + +macro padd($x, z()): + $x + +macro dec(psuc($x)): + dec($x) + 1 + +macro dec(z()): + 0 + +macro pmul($x, z()): + z() + +macro pmul($x, psuc($y)): + padd(pmul($x, $y), $x) + +macro pexp($x, z()): + one() + +macro pexp($x, psuc($y)): + pmul($x, pexp($x, $y)) + +macro fac(z()): + one() + +macro fac(psuc($x)): + pmul(psuc($x), fac($x)) + +macro one(): + psuc(z()) + +macro two(): + psuc(psuc(z())) + +macro three(): + psuc(psuc(psuc(z()))) + +macro five(): + padd(three(), two()) + +return([dec(pmul(three(), pmul(three(), three()))), dec(fac(five()))], 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/returnten.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/returnten.se new file mode 100644 index 000000000..7969c9eb8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/returnten.se @@ -0,0 +1,4 @@ +extern mul2: [double] + +x = create("mul2.se") +return(x.double(5)) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se new file mode 100644 index 000000000..be5d97fc7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se @@ -0,0 +1,33 @@ +def kall(): + argcount = ~calldatasize() / 32 + if argcount == 1: + return(~calldataload(1)) + + args = array(argcount) + ~calldatacopy(args, 1, argcount * 32) + low = array(argcount) + lsz = 0 + high = array(argcount) + hsz = 0 + i = 1 + while i < argcount: + if args[i] < args[0]: + low[lsz] = args[i] + lsz += 1 + else: + high[hsz] = args[i] + hsz += 1 + i += 1 + low = self.kall(data=low, datasz=lsz, outsz=lsz) + high = self.kall(data=high, datasz=hsz, outsz=hsz) + o = array(argcount) + i = 0 + while i < lsz: + o[i] = low[i] + i += 1 + o[lsz] = args[0] + j = 0 + while j < hsz: + o[lsz + 1 + j] = high[j] + j += 1 + return(o, argcount) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se new file mode 100644 index 000000000..0e603a238 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se @@ -0,0 +1,46 @@ +# Quicksort pairs +# eg. input of the form [ 30, 1, 90, 2, 70, 3, 50, 4] +# outputs [ 30, 1, 50, 4, 70, 3, 90, 2 ] +# +# Note: this can be used as a generalized sorting algorithm: +# map every object to [ key, ref ] where `ref` is the index +# in memory to all of the properties and `key` is the key to +# sort by + + +def kall(): + argcount = ~calldatasize() / 64 + if argcount == 1: + return([~calldataload(1), ~calldataload(33)], 2) + + args = array(argcount * 2) + ~calldatacopy(args, 1, argcount * 64) + low = array(argcount * 2) + lsz = 0 + high = array(argcount * 2) + hsz = 0 + i = 2 + while i < argcount * 2: + if args[i] < args[0]: + low[lsz] = args[i] + low[lsz + 1] = args[i + 1] + lsz += 2 + else: + high[hsz] = args[i] + high[hsz + 1] = args[i + 1] + hsz += 2 + i = i + 2 + low = self.kall(data=low, datasz=lsz, outsz=lsz) + high = self.kall(data=high, datasz=hsz, outsz=hsz) + o = array(argcount * 2) + i = 0 + while i < lsz: + o[i] = low[i] + i += 1 + o[lsz] = args[0] + o[lsz + 1] = args[1] + j = 0 + while j < hsz: + o[lsz + 2 + j] = high[j] + j += 1 + return(o, argcount * 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se new file mode 100644 index 000000000..a7d7da9c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se @@ -0,0 +1,94 @@ +# SchellingCoin implementation +# +# Epoch length: 100 blocks +# Target savings depletion rate: 0.1% per epoch + +data epoch +data hashes_submitted +data output +data quicksort_pairs +data accounts[2^160] +data submissions[2^80](hash, deposit, address, value) +extern any: [call] + + +def init(): + self.epoch = block.number / 100 + self.quicksort_pairs = create('quicksort_pairs.se') + +def any(): + if block.number / 100 > epoch: + # Sort all values submitted + N = self.hashes_submitted + o = array(N * 2) + i = 0 + j = 0 + while i < N: + v = self.submissions[i].value + if v: + o[j] = v + o[j + 1] = i + j += 2 + i += 1 + values = self.quicksort_pairs.call(data=o, datasz=j, outsz=j) + + # Calculate total deposit, refund non-submitters and + # cleanup + + deposits = array(j / 2) + addresses = array(j / 2) + + i = 0 + total_deposit = 0 + while i < j / 2: + base_index = HASHES + values[i * 2 + 1] * 3 + deposits[i] = self.submissions[i].deposit + addresses[i] = self.submissions[i].address + if self.submissions[values[i * 2 + 1]].value: + total_deposit += deposits[i] + else: + send(addresses[i], deposits[i] * 999 / 1000) + i += 1 + + inverse_profit_ratio = total_deposit / (contract.balance / 1000) + 1 + + # Reward everyone + i = 0 + running_deposit_sum = 0 + halfway_passed = 0 + while i < j / 2: + new_deposit_sum = running_deposit_sum + deposits[i] + if new_deposit_sum > total_deposit / 4 and running_deposit_sum < total_deposit * 3 / 4: + send(addresses[i], deposits[i] + deposits[i] / inverse_profit_ratio * 2) + else: + send(addresses[i], deposits[i] - deposits[i] / inverse_profit_ratio) + + if not halfway_passed and new_deposit_sum > total_deposit / 2: + self.output = self.submissions[i].value + halfway_passed = 1 + self.submissions[i].value = 0 + running_deposit_sum = new_deposit_sum + i += 1 + self.epoch = block.number / 100 + self.hashes_submitted = 0 + +def submit_hash(h): + if block.number % 100 < 50: + cur = self.hashes_submitted + pos = HASHES + cur * 3 + self.submissions[cur].hash = h + self.submissions[cur].deposit = msg.value + self.submissions[cur].address = msg.sender + self.hashes_submitted = cur + 1 + return(cur) + +def submit_value(index, v): + if sha3([msg.sender, v], 2) == self.submissions[index].hash: + self.submissions[index].value = v + return(1) + +def request_balance(): + return(contract.balance) + +def request_output(): + return(self.output) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se new file mode 100644 index 000000000..a34f42ce2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se @@ -0,0 +1,171 @@ +# Hedged zero-supply dollar implementation +# Uses SchellingCoin as price-determining backend +# +# Stored variables: +# +# 0: Schelling coin contract +# 1: Last epoch +# 2: Genesis block of contract +# 3: USD exposure +# 4: ETH exposure +# 5: Cached price +# 6: Last interest rate +# 2^160 + k: interest rate accumulator at k epochs +# 2^161 + ADDR * 3: eth-balance of a particular address +# 2^161 + ADDR * 3 + 1: usd-balance of a particular address +# 2^161 + ADDR * 3 + 1: last accessed epoch of a particular address +# +# Transaction types: +# +# [1, to, val]: send ETH +# [2, to, val]: send USD +# [3, wei_amount]: convert ETH to USD +# [4, usd_amount]: converts USD to ETH +# [5]: deposit +# [6, amount]: withdraw +# [7]: my balance query +# [7, acct]: balance query for any acct +# [8]: global state query +# [9]: liquidation test any account +# +# The purpose of the contract is to serve as a sort of cryptographic +# bank account where users can store both ETH and USD. ETH must be +# stored in zero or positive quantities, but USD balances can be +# positive or negative. If the USD balance is negative, the invariant +# usdbal * 10 >= ethbal * 9 must be satisfied; if any account falls +# below this value, then that account's balances are zeroed. Note +# that there is a 2% bounty to ping the app if an account does go +# below zero; one weakness is that if no one does ping then it is +# quite possible for accounts to go negative-net-worth, then zero +# themselves out, draining the reserves of the "bank" and potentially +# bankrupting it. A 0.1% fee on ETH <-> USD trade is charged to +# minimize this risk. Additionally, the bank itself will inevitably +# end up with positive or negative USD exposure; to mitigate this, +# it automatically updates interest rates on USD to keep exposure +# near zero. + +data schelling_coin +data last_epoch +data starting_block +data usd_exposure +data eth_exposure +data price +data last_interest_rate +data interest_rate_accum[2^50] +data accounts[2^160](eth, usd, last_epoch) + +extern sc: [submit_hash, submit_value, request_balance, request_output] + +def init(): + self.schelling_coin = create('schellingcoin.se') + self.price = self.schelling_coin.request_output() + self.interest_rate_accum[0] = 10^18 + self.starting_block = block.number + +def any(): + sender = msg.sender + epoch = (block.number - self.starting_block) / 100 + last_epoch = self.last_epoch + usdprice = self.price + + # Update contract epochs + if epoch > last_epoch: + delta = epoch - last_epoch + last_interest_rate = self.last_interest_rate + usd_exposure - self.usd_exposure + last_accum = self.interest_rate_accum[last_epoch] + + if usd_exposure < 0: + self.last_interest_rate = last_interest_rate - 10000 * delta + elif usd_exposure > 0: + self.last_interest_rate = last_interest_rate + 10000 * delta + + self.interest_rate_accum[epoch] = last_accum + last_accum * last_interest_rate * delta / 10^9 + + # Proceeds go to support the SchellingCoin feeding it price data, ultimately providing the depositors + # of the SchellingCoin an interest rate + bal = max(self.balance - self.eth_exposure, 0) / 10000 + usdprice = self.schelling_coin.request_output() + self.price = usdprice + self.last_epoch = epoch + + ethbal = self.accounts[msg.sender].eth + usdbal = self.accounts[msg.sender].usd + + # Apply interest rates to sender and liquidation-test self + if msg.sender != self: + self.ping(self) + +def send_eth(to, value): + if value > 0 and value <= ethbal and usdbal * usdprice * 2 + (ethbal - value) >= 0: + self.accounts[msg.sender].eth = ethbal - value + self.ping(to) + self.accounts[to].eth += value + return(1) + +def send_usd(to, value): + if value > 0 and value <= usdbal and (usdbal - value) * usdprice * 2 + ethbal >= 0: + self.accounts[msg.sender].usd = usdbal - value + self.ping(to) + self.accounts[to].usd += value + return(1) + +def convert_to_eth(usdvalue): + ethplus = usdvalue * usdprice * 999 / 1000 + if usdvalue > 0 and (usdbal - usdvalue) * usdprice * 2 + (ethbal + ethplus) >= 0: + self.accounts[msg.sender].eth = ethbal + ethplus + self.accounts[msg.sender].usd = usdbal - usdvalue + self.eth_exposure += ethplus + self.usd_exposure -= usdvalue + return([ethbal + ethplus, usdbal - usdvalue], 2) + +def convert_to_usd(ethvalue): + usdplus = ethvalue / usdprice * 999 / 1000 + if ethvalue > 0 and (usdbal + usdplus) * usdprice * 2 + (ethbal - ethvalue) >= 0: + self.accounts[msg.sender].eth = ethbal - ethvalue + self.accounts[msg.sender].usd = usdbal + usdplus + self.eth_exposure -= ethvalue + self.usd_exposure += usdplus + return([ethbal - ethvalue, usdbal + usdplus], 2) + +def deposit(): + self.accounts[msg.sender].eth = ethbal + msg.value + self.eth_exposure += msg.value + return(ethbal + msg.value) + +def withdraw(value): + if value > 0 and value <= ethbal and usdbal * usdprice * 2 + (ethbal - value) >= 0: + self.accounts[msg.sender].eth -= value + self.eth_exposure -= value + return(ethbal - value) + +def balance(acct): + self.ping(acct) + return([self.accounts[acct].eth, self.accounts[acct].usd], 2) + +def global_state_query(acct): + interest = self.last_interest_rate + usd_exposure = self.usd_exposure + eth_exposure = self.eth_exposure + eth_balance = self.balance + return([epoch, usdprice, interest, usd_exposure, eth_exposure, eth_balance], 6) + +def ping(acct): + account_last_epoch = self.accounts[acct].last_epoch + if account_last_epoch != epoch: + cur_usd_balance = self.accounts[acct].usd + new_usd_balance = cur_usd_balance * self.interest_rate_accum[epoch] / self.interest_rate_accum[account_last_epoch] + self.accounts[acct].usd = new_usd_balance + self.accounts[acct].last_epoch = epoch + self.usd_exposure += new_usd_balance - cur_usd_balance + + ethbal = self.accounts[acct].eth + + if new_usd_balance * usdval * 10 + ethbal * 9 < 0: + self.accounts[acct].eth = 0 + self.accounts[acct].usd = 0 + self.accounts[msg.sender].eth += ethbal / 50 + self.eth_exposure += -ethbal + ethbal / 50 + self.usd_exposure += new_usd_balance + return(1) + return(0) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellinghelper.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellinghelper.se new file mode 100644 index 000000000..0e522d6e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellinghelper.se @@ -0,0 +1 @@ +return(sha3([msg.sender, msg.data[0]], 2)) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/short_namecoin.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/short_namecoin.se new file mode 100644 index 000000000..db327a77d --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/short_namecoin.se @@ -0,0 +1,3 @@ +def register(k, v): + if !self.storage[k]: + self.storage[k] = v diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/subcurrency.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/subcurrency.se new file mode 100644 index 000000000..fbda822b6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/subcurrency.se @@ -0,0 +1,11 @@ +def init(): + self.storage[msg.sender] = 1000000 + +def balance_query(k): + return(self.storage[addr]) + +def send(to, value): + fromvalue = self.storage[msg.sender] + if fromvalue >= value: + self.storage[from] = fromvalue - value + self.storage[to] += value diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.cpp new file mode 100644 index 000000000..ea9be14a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include "funcs.h" +#include "bignum.h" +#include "util.h" +#include "parser.h" +#include "lllparser.h" +#include "compiler.h" +#include "rewriter.h" +#include "tokenize.h" + +Node compileToLLL(std::string input) { + return rewrite(parseSerpent(input)); +} + +Node compileChunkToLLL(std::string input) { + return rewriteChunk(parseSerpent(input)); +} + +std::string compile(std::string input) { + return compileLLL(compileToLLL(input)); +} + +std::vector prettyCompile(std::string input) { + return prettyCompileLLL(compileToLLL(input)); +} + +std::string compileChunk(std::string input) { + return compileLLL(compileChunkToLLL(input)); +} + +std::vector prettyCompileChunk(std::string input) { + return prettyCompileLLL(compileChunkToLLL(input)); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.h new file mode 100644 index 000000000..d9bf44549 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/funcs.h @@ -0,0 +1,35 @@ +#include +#include +#include +#include "bignum.h" +#include "util.h" +#include "parser.h" +#include "lllparser.h" +#include "compiler.h" +#include "rewriter.h" +#include "tokenize.h" + +// Function listing: +// +// parseSerpent (serpent -> AST) std::string -> Node +// parseLLL (LLL -> AST) std::string -> Node +// rewrite (apply rewrite rules) Node -> Node +// compileToLLL (serpent -> LLL) std::string -> Node +// compileLLL (LLL -> EVMhex) Node -> std::string +// prettyCompileLLL (LLL -> EVMasm) Node -> std::vector +// prettyCompile (serpent -> EVMasm) std::string -> std::vector>Node> +// compile (serpent -> EVMhex) std::string -> std::string +// get_file_contents (filename -> file) std::string -> std::string +// exists (does file exist?) std::string -> bool + +Node compileToLLL(std::string input); + +Node compileChunkToLLL(std::string input); + +std::string compile(std::string input); + +std::vector prettyCompile(std::string input); + +std::string compileChunk(std::string input); + +std::vector prettyCompileChunk(std::string input); diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.cpp new file mode 100644 index 000000000..78e12e84a --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" +#include "functions.h" + +std::string getSignature(std::vector args) { + std::string o; + for (unsigned i = 0; i < args.size(); i++) { + if (args[i].val == ":" && args[i].args[1].val == "s") + o += "s"; + else if (args[i].val == ":" && args[i].args[1].val == "a") + o += "a"; + else + o += "i"; + } + return o; +} + +// Convert a list of arguments into a node containing a +// < datastart, datasz > pair + +Node packArguments(std::vector args, std::string sig, + int funId, Metadata m) { + // Plain old 32 byte arguments + std::vector nargs; + // Variable-sized arguments + std::vector vargs; + // Variable sizes + std::vector sizes; + // Is a variable an array? + std::vector isArray; + // Fill up above three argument lists + int argCount = 0; + for (unsigned i = 0; i < args.size(); i++) { + Metadata m = args[i].metadata; + if (args[i].val == "=") { + // do nothing + } + else { + // Determine the correct argument type + char argType; + if (sig.size() > 0) { + if (argCount >= (signed)sig.size()) + err("Too many args", m); + argType = sig[argCount]; + } + else argType = 'i'; + // Integer (also usable for short strings) + if (argType == 'i') { + if (args[i].val == ":") + err("Function asks for int, provided string or array", m); + nargs.push_back(args[i]); + } + // Long string + else if (argType == 's') { + if (args[i].val != ":") + err("Must specify string length", m); + vargs.push_back(args[i].args[0]); + sizes.push_back(args[i].args[1]); + isArray.push_back(false); + } + // Array + else if (argType == 'a') { + if (args[i].val != ":") + err("Must specify array length", m); + vargs.push_back(args[i].args[0]); + sizes.push_back(args[i].args[1]); + isArray.push_back(true); + } + else err("Invalid arg type in signature", m); + argCount++; + } + } + int static_arg_size = 1 + (vargs.size() + nargs.size()) * 32; + // Start off by saving the size variables and calculating the total + msn kwargs; + kwargs["funid"] = tkn(utd(funId), m); + std::string pattern = + "(with _sztot "+utd(static_arg_size)+" " + " (with _sizes (alloc "+utd(sizes.size() * 32)+") " + " (seq "; + for (unsigned i = 0; i < sizes.size(); i++) { + std::string sizeIncrement = + isArray[i] ? "(mul 32 _x)" : "_x"; + pattern += + "(with _x $sz"+utd(i)+"(seq " + " (mstore (add _sizes "+utd(i * 32)+") _x) " + " (set _sztot (add _sztot "+sizeIncrement+" )))) "; + kwargs["sz"+utd(i)] = sizes[i]; + } + // Allocate memory, and set first data byte + pattern += + "(with _datastart (alloc (add _sztot 32)) (seq " + " (mstore8 _datastart $funid) "; + // Copy over size variables + for (unsigned i = 0; i < sizes.size(); i++) { + int v = 1 + i * 32; + pattern += + " (mstore " + " (add _datastart "+utd(v)+") " + " (mload (add _sizes "+utd(v-1)+"))) "; + } + // Store normal arguments + for (unsigned i = 0; i < nargs.size(); i++) { + int v = 1 + (i + sizes.size()) * 32; + pattern += + " (mstore (add _datastart "+utd(v)+") $"+utd(i)+") "; + kwargs[utd(i)] = nargs[i]; + } + // Loop through variable-sized arguments, store them + pattern += + " (with _pos (add _datastart "+utd(static_arg_size)+") (seq"; + for (unsigned i = 0; i < vargs.size(); i++) { + std::string copySize = + isArray[i] ? "(mul 32 (mload (add _sizes "+utd(i * 32)+")))" + : "(mload (add _sizes "+utd(i * 32)+"))"; + pattern += + " (unsafe_mcopy _pos $vl"+utd(i)+" "+copySize+") " + " (set _pos (add _pos "+copySize+")) "; + kwargs["vl"+utd(i)] = vargs[i]; + } + // Return a 2-item array containing the start and size + pattern += " (array_lit _datastart _sztot))))))))"; + std::string prefix = "_temp_"+mkUniqueToken(); + // Fill in pattern, return triple + return subst(parseLLL(pattern), kwargs, prefix, m); +} + +// Create a node for argument unpacking +Node unpackArguments(std::vector vars, Metadata m) { + std::vector varNames; + std::vector longVarNames; + std::vector longVarIsArray; + // Fill in variable and long variable names, as well as which + // long variables are arrays and which are strings + for (unsigned i = 0; i < vars.size(); i++) { + if (vars[i].val == ":") { + if (vars[i].args.size() != 2) + err("Malformed def!", m); + longVarNames.push_back(vars[i].args[0].val); + std::string tag = vars[i].args[1].val; + if (tag == "s") + longVarIsArray.push_back(false); + else if (tag == "a") + longVarIsArray.push_back(true); + else + err("Function value can only be string or array", m); + } + else { + varNames.push_back(vars[i].val); + } + } + std::vector sub; + if (!varNames.size() && !longVarNames.size()) { + // do nothing if we have no arguments + } + else { + std::vector varNodes; + for (unsigned i = 0; i < longVarNames.size(); i++) + varNodes.push_back(token(longVarNames[i], m)); + for (unsigned i = 0; i < varNames.size(); i++) + varNodes.push_back(token(varNames[i], m)); + // Copy over variable lengths and short variables + for (unsigned i = 0; i < varNodes.size(); i++) { + int pos = 1 + i * 32; + std::string prefix = (i < longVarNames.size()) ? "_len_" : ""; + sub.push_back(asn("untyped", asn("set", + token(prefix+varNodes[i].val, m), + asn("calldataload", tkn(utd(pos), m), m), + m))); + } + // Copy over long variables + if (longVarNames.size() > 0) { + std::vector sub2; + int pos = varNodes.size() * 32 + 1; + Node tot = tkn("_tot", m); + for (unsigned i = 0; i < longVarNames.size(); i++) { + Node var = tkn(longVarNames[i], m); + Node varlen = longVarIsArray[i] + ? asn("mul", tkn("32", m), tkn("_len_"+longVarNames[i], m)) + : tkn("_len_"+longVarNames[i], m); + sub2.push_back(asn("untyped", + asn("set", var, asn("alloc", varlen)))); + sub2.push_back(asn("calldatacopy", var, tot, varlen)); + sub2.push_back(asn("set", tot, asn("add", tot, varlen))); + } + std::string prefix = "_temp_"+mkUniqueToken(); + sub.push_back(subst( + astnode("with", tot, tkn(utd(pos), m), asn("seq", sub2)), + msn(), + prefix, + m)); + } + } + return asn("seq", sub, m); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.h new file mode 100644 index 000000000..68a1c69ce --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/functions.h @@ -0,0 +1,39 @@ +#ifndef ETHSERP_FUNCTIONS +#define ETHSERP_FUNCTIONS + +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" + + +class argPack { + public: + argPack(Node a, Node b, Node c) { + pre = a; + datastart = b; + datasz = c; + } + Node pre; + Node datastart; + Node datasz; +}; + +// Get a signature from a function +std::string getSignature(std::vector args); + +// Convert a list of arguments into a node +// triple, given the signature of a function +Node packArguments(std::vector args, std::string sig, + int funId, Metadata m); + +// Create a node for argument unpacking +Node unpackArguments(std::vector vars, Metadata m); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.cpp new file mode 100644 index 000000000..ad4fbd52d --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "tokenize.h" + +struct _parseOutput { + Node node; + int newpos; +}; + +// Helper, returns subtree and position of start of next node +_parseOutput _parse(std::vector inp, int pos) { + Metadata met = inp[pos].metadata; + _parseOutput o; + // Bracket: keep grabbing tokens until we get to the + // corresponding closing bracket + if (inp[pos].val == "(" || inp[pos].val == "[") { + std::string fun, rbrack; + std::vector args; + pos += 1; + if (inp[pos].val == "[") { + fun = "access"; + rbrack = "]"; + } + else rbrack = ")"; + // First argument is the function + while (inp[pos].val != ")") { + _parseOutput po = _parse(inp, pos); + if (fun.length() == 0 && po.node.type == 1) { + std::cerr << "Error: first arg must be function\n"; + fun = po.node.val; + } + else if (fun.length() == 0) { + fun = po.node.val; + } + else { + args.push_back(po.node); + } + pos = po.newpos; + } + o.newpos = pos + 1; + o.node = astnode(fun, args, met); + } + // Normal token, return it and advance to next token + else { + o.newpos = pos + 1; + o.node = token(inp[pos].val, met); + } + return o; +} + +// stream of tokens -> lisp parse tree +Node parseLLLTokenStream(std::vector inp) { + _parseOutput o = _parse(inp, 0); + return o.node; +} + +// Parses LLL +Node parseLLL(std::string s, bool allowFileRead) { + std::string input = s; + std::string file = "main"; + if (exists(s) && allowFileRead) { + file = s; + input = get_file_contents(s); + } + return parseLLLTokenStream(tokenize(s, Metadata(file, 0, 0), true)); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.h new file mode 100644 index 000000000..4bfa7b82e --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/lllparser.h @@ -0,0 +1,13 @@ +#ifndef ETHSERP_LLLPARSER +#define ETHSERP_LLLPARSER + +#include +#include +#include +#include +#include "util.h" + +// LLL text -> parse tree +Node parseLLL(std::string s, bool allowFileRead=false); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.cpp new file mode 100644 index 000000000..b24144e46 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include "opcodes.h" +#include "util.h" +#include "bignum.h" + +Mapping mapping[] = { + Mapping("STOP", 0x00, 0, 0), + Mapping("ADD", 0x01, 2, 1), + Mapping("MUL", 0x02, 2, 1), + Mapping("SUB", 0x03, 2, 1), + Mapping("DIV", 0x04, 2, 1), + Mapping("SDIV", 0x05, 2, 1), + Mapping("MOD", 0x06, 2, 1), + Mapping("SMOD", 0x07, 2, 1), + Mapping("ADDMOD", 0x08, 3, 1), + Mapping("MULMOD", 0x09, 3, 1), + Mapping("EXP", 0x0a, 2, 1), + Mapping("SIGNEXTEND", 0x0b, 2, 1), + Mapping("LT", 0x10, 2, 1), + Mapping("GT", 0x11, 2, 1), + Mapping("SLT", 0x12, 2, 1), + Mapping("SGT", 0x13, 2, 1), + Mapping("EQ", 0x14, 2, 1), + Mapping("ISZERO", 0x15, 1, 1), + Mapping("AND", 0x16, 2, 1), + Mapping("OR", 0x17, 2, 1), + Mapping("XOR", 0x18, 2, 1), + Mapping("NOT", 0x19, 1, 1), + Mapping("BYTE", 0x1a, 2, 1), + Mapping("SHA3", 0x20, 2, 1), + Mapping("ADDRESS", 0x30, 0, 1), + Mapping("BALANCE", 0x31, 1, 1), + Mapping("ORIGIN", 0x32, 0, 1), + Mapping("CALLER", 0x33, 0, 1), + Mapping("CALLVALUE", 0x34, 0, 1), + Mapping("CALLDATALOAD", 0x35, 1, 1), + Mapping("CALLDATASIZE", 0x36, 0, 1), + Mapping("CALLDATACOPY", 0x37, 3, 0), + Mapping("CODESIZE", 0x38, 0, 1), + Mapping("CODECOPY", 0x39, 3, 0), + Mapping("GASPRICE", 0x3a, 0, 1), + Mapping("EXTCODESIZE", 0x3b, 1, 1), + Mapping("EXTCODECOPY", 0x3c, 4, 0), + Mapping("PREVHASH", 0x40, 0, 1), + Mapping("COINBASE", 0x41, 0, 1), + Mapping("TIMESTAMP", 0x42, 0, 1), + Mapping("NUMBER", 0x43, 0, 1), + Mapping("DIFFICULTY", 0x44, 0, 1), + Mapping("GASLIMIT", 0x45, 0, 1), + Mapping("POP", 0x50, 1, 0), + Mapping("MLOAD", 0x51, 1, 1), + Mapping("MSTORE", 0x52, 2, 0), + Mapping("MSTORE8", 0x53, 2, 0), + Mapping("SLOAD", 0x54, 1, 1), + Mapping("SSTORE", 0x55, 2, 0), + Mapping("JUMP", 0x56, 1, 0), + Mapping("JUMPI", 0x57, 2, 0), + Mapping("PC", 0x58, 0, 1), + Mapping("MSIZE", 0x59, 0, 1), + Mapping("GAS", 0x5a, 0, 1), + Mapping("JUMPDEST", 0x5b, 0, 0), + Mapping("LOG0", 0xa0, 2, 0), + Mapping("LOG1", 0xa1, 3, 0), + Mapping("LOG2", 0xa2, 4, 0), + Mapping("LOG3", 0xa3, 5, 0), + Mapping("LOG4", 0xa4, 6, 0), + Mapping("CREATE", 0xf0, 3, 1), + Mapping("CALL", 0xf1, 7, 1), + Mapping("CALLCODE", 0xf2, 7, 1), + Mapping("RETURN", 0xf3, 2, 0), + Mapping("SUICIDE", 0xff, 1, 0), + Mapping("---END---", 0x00, 0, 0), +}; + +std::map > opcodes; +std::map reverseOpcodes; + +// Fetches everything EXCEPT PUSH1..32 +std::pair > _opdata(std::string ops, int opi) { + if (!opcodes.size()) { + int i = 0; + while (mapping[i].op != "---END---") { + Mapping mi = mapping[i]; + opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); + i++; + } + for (i = 1; i <= 16; i++) { + opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); + opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); + } + for (std::map >::iterator it=opcodes.begin(); + it != opcodes.end(); + it++) { + reverseOpcodes[(*it).second[0]] = (*it).first; + } + } + ops = upperCase(ops); + std::string op; + std::vector opdata; + op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; + opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); + return std::pair >(op, opdata); +} + +int opcode(std::string op) { + return _opdata(op, -1).second[0]; +} + +int opinputs(std::string op) { + return _opdata(op, -1).second[1]; +} + +int opoutputs(std::string op) { + return _opdata(op, -1).second[2]; +} + +std::string op(int opcode) { + return _opdata("", opcode).first; +} + +std::string lllSpecials[][3] = { + { "ref", "1", "1" }, + { "get", "1", "1" }, + { "set", "2", "2" }, + { "with", "3", "3" }, + { "comment", "0", "2147483647" }, + { "ops", "0", "2147483647" }, + { "lll", "2", "2" }, + { "seq", "0", "2147483647" }, + { "if", "3", "3" }, + { "unless", "2", "2" }, + { "until", "2", "2" }, + { "alloc", "1", "1" }, + { "---END---", "0", "0" }, +}; + +std::map > lllMap; + +// Is a function name one of the valid functions above? +bool isValidLLLFunc(std::string f, int argc) { + if (lllMap.size() == 0) { + for (int i = 0; ; i++) { + if (lllSpecials[i][0] == "---END---") break; + lllMap[lllSpecials[i][0]] = std::pair( + dtu(lllSpecials[i][1]), dtu(lllSpecials[i][2])); + } + } + return lllMap.count(f) + && argc >= lllMap[f].first + && argc <= lllMap[f].second; +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.h new file mode 100644 index 000000000..41423c169 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/opcodes.h @@ -0,0 +1,45 @@ +#ifndef ETHSERP_OPCODES +#define ETHSERP_OPCODES + +#include +#include +#include +#include +#include "util.h" + +class Mapping { + public: + Mapping(std::string Op, int Opcode, int In, int Out) { + op = Op; + opcode = Opcode; + in = In; + out = Out; + } + std::string op; + int opcode; + int in; + int out; +}; + +extern Mapping mapping[]; + +extern std::map > opcodes; +extern std::map reverseOpcodes; + +std::pair > _opdata(std::string ops, int opi); + +int opcode(std::string op); + +int opinputs(std::string op); + +int opoutputs(std::string op); + +std::string op(int opcode); + +extern std::string lllSpecials[][3]; + +extern std::map > lllMap; + +bool isValidLLLFunc(std::string f, int argc); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.cpp new file mode 100644 index 000000000..e689fcb69 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" + +// Compile-time arithmetic calculations +Node optimize(Node inp) { + if (inp.type == TOKEN) { + Node o = tryNumberize(inp); + if (decimalGt(o.val, tt256, true)) + err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata); + return o; + } + for (unsigned i = 0; i < inp.args.size(); i++) { + inp.args[i] = optimize(inp.args[i]); + } + // Arithmetic-specific transform + if (inp.val == "+") inp.val = "add"; + if (inp.val == "*") inp.val = "mul"; + if (inp.val == "-") inp.val = "sub"; + if (inp.val == "/") inp.val = "sdiv"; + if (inp.val == "^") inp.val = "exp"; + if (inp.val == "**") inp.val = "exp"; + if (inp.val == "%") inp.val = "smod"; + // Degenerate cases for add and mul + if (inp.args.size() == 2) { + if (inp.val == "add" && inp.args[0].type == TOKEN && + inp.args[0].val == "0") { + Node x = inp.args[1]; + inp = x; + } + if (inp.val == "add" && inp.args[1].type == TOKEN && + inp.args[1].val == "0") { + Node x = inp.args[0]; + inp = x; + } + if (inp.val == "mul" && inp.args[0].type == TOKEN && + inp.args[0].val == "1") { + Node x = inp.args[1]; + inp = x; + } + if (inp.val == "mul" && inp.args[1].type == TOKEN && + inp.args[1].val == "1") { + Node x = inp.args[0]; + inp = x; + } + } + // Arithmetic computation + if (inp.args.size() == 2 + && inp.args[0].type == TOKEN + && inp.args[1].type == TOKEN) { + std::string o; + if (inp.val == "add") { + o = decimalMod(decimalAdd(inp.args[0].val, inp.args[1].val), tt256); + } + else if (inp.val == "sub") { + if (decimalGt(inp.args[0].val, inp.args[1].val, true)) + o = decimalSub(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "mul") { + o = decimalMod(decimalMul(inp.args[0].val, inp.args[1].val), tt256); + } + else if (inp.val == "div" && inp.args[1].val != "0") { + o = decimalDiv(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "sdiv" && inp.args[1].val != "0" + && decimalGt(tt255, inp.args[0].val) + && decimalGt(tt255, inp.args[1].val)) { + o = decimalDiv(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "mod" && inp.args[1].val != "0") { + o = decimalMod(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "smod" && inp.args[1].val != "0" + && decimalGt(tt255, inp.args[0].val) + && decimalGt(tt255, inp.args[1].val)) { + o = decimalMod(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "exp") { + o = decimalModExp(inp.args[0].val, inp.args[1].val, tt256); + } + if (o.length()) return token(o, inp.metadata); + } + return inp; +} + +// Is a node degenerate (ie. trivial to calculate) ? +bool isDegenerate(Node n) { + return optimize(n).type == TOKEN; +} + +// Is a node purely arithmetic? +bool isPureArithmetic(Node n) { + return isNumberLike(optimize(n)); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.h new file mode 100644 index 000000000..06ea3bba1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/optimize.h @@ -0,0 +1,19 @@ +#ifndef ETHSERP_OPTIMIZER +#define ETHSERP_OPTIMIZER + +#include +#include +#include +#include +#include "util.h" + +// Compile-time arithmetic calculations +Node optimize(Node inp); + +// Is a node degenerate (ie. trivial to calculate) ? +bool isDegenerate(Node n); + +// Is a node purely arithmetic? +bool isPureArithmetic(Node n); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.cpp new file mode 100644 index 000000000..5e8c459c3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.cpp @@ -0,0 +1,430 @@ +#include +#include +#include +#include +#include "util.h" +#include "parser.h" +#include "tokenize.h" + +// Extended BEDMAS precedence order +int precedence(Node tok) { + std::string v = tok.val; + if (v == ".") return -1; + else if (v == "!" || v == "not") return 1; + else if (v=="^" || v == "**") return 2; + else if (v=="*" || v=="/" || v=="%") return 3; + else if (v=="+" || v=="-") return 4; + else if (v=="<" || v==">" || v=="<=" || v==">=") return 5; + else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6; + else if (v=="&&" || v=="and") return 7; + else if (v=="||" || v=="or") return 8; + else if (v=="=") return 10; + else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10; + else if (v==":" || v == "::") return 11; + else return 0; +} + +// Token classification for shunting-yard purposes +int toktype(Node tok) { + if (tok.type == ASTNODE) return COMPOUND; + std::string v = tok.val; + if (v == "(" || v == "[" || v == "{") return LPAREN; + else if (v == ")" || v == "]" || v == "}") return RPAREN; + else if (v == ",") return COMMA; + else if (v == "!" || v == "~" || v == "not") return UNARY_OP; + else if (precedence(tok) > 0) return BINARY_OP; + else if (precedence(tok) < 0) return TOKEN_SPLITTER; + if (tok.val[0] != '"' && tok.val[0] != '\'') { + for (unsigned i = 0; i < tok.val.length(); i++) { + if (chartype(tok.val[i]) == SYMB) { + err("Invalid symbol: "+tok.val, tok.metadata); + } + } + } + return ALPHANUM; +} + + +// Converts to reverse polish notation +std::vector shuntingYard(std::vector tokens) { + std::vector iq; + for (int i = tokens.size() - 1; i >= 0; i--) { + iq.push_back(tokens[i]); + } + std::vector oq; + std::vector stack; + Node prev, tok; + int prevtyp = 0, toktyp = 0; + + while (iq.size()) { + prev = tok; + prevtyp = toktyp; + tok = iq.back(); + toktyp = toktype(tok); + iq.pop_back(); + // Alphanumerics go straight to output queue + if (toktyp == ALPHANUM) { + oq.push_back(tok); + } + // Left parens go on stack and output queue + else if (toktyp == LPAREN) { + while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) { + oq.push_back(stack.back()); + stack.pop_back(); + } + if (prevtyp != ALPHANUM && prevtyp != RPAREN) { + oq.push_back(token("id", tok.metadata)); + } + stack.push_back(tok); + oq.push_back(tok); + } + // If rparen, keep moving from stack to output queue until lparen + else if (toktyp == RPAREN) { + while (stack.size() && toktype(stack.back()) != LPAREN) { + oq.push_back(stack.back()); + stack.pop_back(); + } + if (stack.size()) { + stack.pop_back(); + } + oq.push_back(tok); + } + else if (toktyp == UNARY_OP) { + stack.push_back(tok); + } + // If token splitter, just push it to the stack + else if (toktyp == TOKEN_SPLITTER) { + while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) { + oq.push_back(stack.back()); + stack.pop_back(); + } + stack.push_back(tok); + } + // If binary op, keep popping from stack while higher bedmas precedence + else if (toktyp == BINARY_OP) { + if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) { + stack.push_back(tok); + oq.push_back(token("0", tok.metadata)); + } + else { + int prec = precedence(tok); + while (stack.size() + && (toktype(stack.back()) == BINARY_OP + || toktype(stack.back()) == UNARY_OP + || toktype(stack.back()) == TOKEN_SPLITTER) + && precedence(stack.back()) <= prec) { + oq.push_back(stack.back()); + stack.pop_back(); + } + stack.push_back(tok); + } + } + // Comma means finish evaluating the argument + else if (toktyp == COMMA) { + while (stack.size() && toktype(stack.back()) != LPAREN) { + oq.push_back(stack.back()); + stack.pop_back(); + } + } + } + while (stack.size()) { + oq.push_back(stack.back()); + stack.pop_back(); + } + return oq; +} + +// Converts reverse polish notation into tree +Node treefy(std::vector stream) { + std::vector iq; + for (int i = stream.size() -1; i >= 0; i--) { + iq.push_back(stream[i]); + } + std::vector oq; + while (iq.size()) { + Node tok = iq.back(); + iq.pop_back(); + int typ = toktype(tok); + // If unary, take node off end of oq and wrap it with the operator + // If binary, do the same with two nodes + if (typ == UNARY_OP || typ == BINARY_OP || typ == TOKEN_SPLITTER) { + std::vector args; + int rounds = (typ == UNARY_OP) ? 1 : 2; + for (int i = 0; i < rounds; i++) { + if (oq.size() == 0) { + err("Line malformed, not enough args for "+tok.val, + tok.metadata); + } + args.push_back(oq.back()); + oq.pop_back(); + } + std::vector args2; + while (args.size()) { + args2.push_back(args.back()); + args.pop_back(); + } + oq.push_back(astnode(tok.val, args2, tok.metadata)); + } + // If rparen, keep grabbing until we get to an lparen + else if (typ == RPAREN) { + std::vector args; + while (1) { + if (toktype(oq.back()) == LPAREN) break; + args.push_back(oq.back()); + oq.pop_back(); + if (!oq.size()) err("Bracket without matching", tok.metadata); + } + oq.pop_back(); + args.push_back(oq.back()); + oq.pop_back(); + // We represent a[b] as (access a b) + if (tok.val == "]") + args.push_back(token("access", tok.metadata)); + if (args.back().type == ASTNODE) + args.push_back(token("fun", tok.metadata)); + std::string fun = args.back().val; + args.pop_back(); + // We represent [1,2,3] as (array_lit 1 2 3) + if (fun == "access" && args.size() && args.back().val == "id") { + fun = "array_lit"; + args.pop_back(); + } + std::vector args2; + while (args.size()) { + args2.push_back(args.back()); + args.pop_back(); + } + // When evaluating 2 + (3 * 5), the shunting yard algo turns that + // into 2 ( id 3 5 * ) +, effectively putting "id" as a dummy + // function where the algo was expecting a function to call the + // thing inside the brackets. This reverses that step + if (fun == "id" && args2.size() == 1) { + oq.push_back(args2[0]); + } + else { + oq.push_back(astnode(fun, args2, tok.metadata)); + } + } + else oq.push_back(tok); + // This is messy, but has to be done. Import/inset other files here + std::string v = oq.back().val; + if ((v == "inset" || v == "import" || v == "create") + && oq.back().args.size() == 1 + && oq.back().args[0].type == TOKEN) { + int lastSlashPos = tok.metadata.file.rfind("/"); + std::string root; + if (lastSlashPos >= 0) + root = tok.metadata.file.substr(0, lastSlashPos) + "/"; + else + root = ""; + std::string filename = oq.back().args[0].val; + filename = filename.substr(1, filename.length() - 2); + if (!exists(root + filename)) + err("File does not exist: "+root + filename, tok.metadata); + oq.back().args.pop_back(); + oq.back().args.push_back(parseSerpent(root + filename)); + } + //Useful for debugging + //for (int i = 0; i < oq.size(); i++) { + // std::cerr << printSimple(oq[i]) << " "; + //} + //std::cerr << " <-\n"; + } + // Output must have one argument + if (oq.size() == 0) { + err("Output blank", Metadata()); + } + else if (oq.size() > 1) { + return asn("multi", oq, oq[0].metadata); + } + + return oq[0]; +} + + +// Parses one line of serpent +Node parseSerpentTokenStream(std::vector s) { + return treefy(shuntingYard(s)); +} + + +// Count spaces at beginning of line +int spaceCount(std::string s) { + unsigned pos = 0; + while (pos < s.length() && (s[pos] == ' ' || s[pos] == '\t')) + pos++; + return pos; +} + +// Is this a command that takes an argument on the same line? +bool bodied(std::string tok) { + return tok == "if" || tok == "elif" || tok == "while" + || tok == "with" || tok == "def" || tok == "extern" + || tok == "data" || tok == "assert" || tok == "return" + || tok == "fun" || tok == "scope" || tok == "macro" + || tok == "type"; +} + +// Are the two commands meant to continue each other? +bool bodiedContinued(std::string prev, std::string tok) { + return (prev == "if" && tok == "elif") + || (prev == "elif" && tok == "else") + || (prev == "elif" && tok == "elif") + || (prev == "if" && tok == "else"); +} + +// Is a line of code empty? +bool isLineEmpty(std::string line) { + std::vector tokens = tokenize(line); + if (!tokens.size() || tokens[0].val == "#" || tokens[0].val == "//") + return true; + return false; +} + +// Parse lines of serpent (helper function) +Node parseLines(std::vector lines, Metadata metadata, int sp) { + std::vector o; + int origLine = metadata.ln; + unsigned i = 0; + while (i < lines.size()) { + metadata.ln = origLine + i; + std::string main = lines[i]; + if (isLineEmpty(main)) { + i += 1; + continue; + } + int spaces = spaceCount(main); + if (spaces != sp) { + err("Indent mismatch", metadata); + } + // Tokenize current line + std::vector tokens = tokenize(main.substr(sp), metadata); + // Remove comments + std::vector tokens2; + for (unsigned j = 0; j < tokens.size(); j++) { + if (tokens[j].val == "#" || tokens[j].val == "//") break; + tokens2.push_back(tokens[j]); + } + bool expectingChildBlock = false; + if (tokens2.size() > 0 && tokens2.back().val == ":") { + tokens2.pop_back(); + expectingChildBlock = true; + } + // Parse current line + Node out = parseSerpentTokenStream(tokens2); + // Parse child block + int childIndent = 999999; + std::vector childBlock; + while (1) { + i++; + if (i >= lines.size()) + break; + bool ile = isLineEmpty(lines[i]); + if (!ile) { + int spaces = spaceCount(lines[i]); + if (spaces <= sp) break; + childBlock.push_back(lines[i]); + if (spaces < childIndent) childIndent = spaces; + } + else childBlock.push_back(""); + } + // Child block empty? + bool cbe = true; + for (unsigned i = 0; i < childBlock.size(); i++) { + if (childBlock[i].length() > 0) { cbe = false; break; } + } + // Add child block to AST + if (expectingChildBlock) { + if (cbe) + err("Expected indented child block!", out.metadata); + out.type = ASTNODE; + metadata.ln += 1; + out.args.push_back(parseLines(childBlock, metadata, childIndent)); + metadata.ln -= 1; + } + else if (!cbe) + err("Did not expect indented child block!", out.metadata); + else if (out.args.size() && out.args[out.args.size() - 1].val == ":") { + Node n = out.args[out.args.size() - 1]; + out.args.pop_back(); + out.args.push_back(n.args[0]); + out.args.push_back(n.args[1]); + } + // Bring back if / elif into AST + if (bodied(tokens[0].val)) { + if (out.val != "multi") { + // token not being used in bodied form + } + else if (out.args[0].val == "id") + out = astnode(tokens[0].val, out.args[1].args, out.metadata); + else if (out.args[0].type == TOKEN) { + std::vector out2; + for (unsigned i = 1; i < out.args.size(); i++) + out2.push_back(out.args[i]); + out = astnode(tokens[0].val, out2, out.metadata); + } + else + out = astnode("fun", out.args, out.metadata); + } + // Multi not supported + if (out.val == "multi") + err("Multiple expressions or unclosed bracket", out.metadata); + // Convert top-level colon expressions into non-colon expressions; + // makes if statements and the like equivalent indented or not + //if (out.val == ":" && out.args[0].type == TOKEN) + // out = asn(out.args[0].val, out.args[1], out.metadata); + //if (bodied(tokens[0].val) && out.args[0].val == ":") + // out = asn(tokens[0].val, out.args[0].args); + if (o.size() == 0 || o.back().type == TOKEN) { + o.push_back(out); + continue; + } + // This is a little complicated. Basically, the idea here is to build + // constructions like [if [< x 5] [a] [elif [< x 10] [b] [else [c]]]] + std::vector u; + u.push_back(o.back()); + if (bodiedContinued(o.back().val, out.val)) { + while (1) { + if (!bodiedContinued(u.back().val, out.val)) { + u.pop_back(); + break; + } + if (!u.back().args.size() + || !bodiedContinued(u.back().val, u.back().args.back().val)) { + break; + } + u.push_back(u.back().args.back()); + } + u.back().args.push_back(out); + while (u.size() > 1) { + Node v = u.back(); + u.pop_back(); + u.back().args.pop_back(); + u.back().args.push_back(v); + } + o.pop_back(); + o.push_back(u[0]); + } + else o.push_back(out); + } + if (o.size() == 1) + return o[0]; + else if (o.size()) + return astnode("seq", o, o[0].metadata); + else + return astnode("seq", o, Metadata()); +} + +// Parses serpent code +Node parseSerpent(std::string s) { + std::string input = s; + std::string file = "main"; + if (exists(s)) { + file = s; + input = get_file_contents(s); + } + return parseLines(splitLines(input), Metadata(file, 0, 0), 0); +} + + +using namespace std; diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.h new file mode 100644 index 000000000..e3632220a --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/parser.h @@ -0,0 +1,13 @@ +#ifndef ETHSERP_PARSER +#define ETHSERP_PARSER + +#include +#include +#include +#include +#include "util.h" + +// Serpent text -> parse tree +Node parseSerpent(std::string s); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.cpp new file mode 100644 index 000000000..3f08ea8b1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.cpp @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "rewriteutils.h" +#include "optimize.h" +#include "preprocess.h" +#include "functions.h" +#include "opcodes.h" + +// Convert a function of the form (def (f x y z) (do stuff)) into +// (if (first byte of ABI is correct) (seq (setup x y z) (do stuff))) +Node convFunction(Node node, int functionCount) { + std::string prefix = "_temp"+mkUniqueToken()+"_"; + Metadata m = node.metadata; + + if (node.args.size() != 2) + err("Malformed def!", m); + // Collect the list of variable names and variable byte counts + Node unpack = unpackArguments(node.args[0].args, m); + // And the actual code + Node body = node.args[1]; + // Main LLL-based function body + return astnode("if", + astnode("eq", + astnode("get", token("__funid", m), m), + token(unsignedToDecimal(functionCount), m), + m), + astnode("seq", unpack, body, m)); +} + +// Populate an svObj with the arguments needed to determine +// the storage position of a node +svObj getStorageVars(svObj pre, Node node, std::string prefix, + int index) { + Metadata m = node.metadata; + if (!pre.globalOffset.size()) pre.globalOffset = "0"; + std::vector h; + std::vector coefficients; + // Array accesses or atoms + if (node.val == "access" || node.type == TOKEN) { + std::string tot = "1"; + h = listfyStorageAccess(node); + coefficients.push_back("1"); + for (unsigned i = h.size() - 1; i >= 1; i--) { + // Array sizes must be constant or at least arithmetically + // evaluable at compile time + if (!isPureArithmetic(h[i])) + err("Array size must be fixed value", m); + // Create a list of the coefficient associated with each + // array index + coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); + } + } + // Tuples + else { + int startc; + // Handle the (fun args...) case + if (node.val == "fun") { + startc = 1; + h = listfyStorageAccess(node.args[0]); + } + // Handle the ( args...) case, which + // the serpent parser produces when the function + // is a simple name and not a complex astnode + else { + startc = 0; + h = listfyStorageAccess(token(node.val, m)); + } + svObj sub = pre; + sub.globalOffset = "0"; + // Evaluate tuple elements recursively + for (unsigned i = startc; i < node.args.size(); i++) { + sub = getStorageVars(sub, + node.args[i], + prefix+h[0].val.substr(2)+".", + i-startc); + } + coefficients.push_back(sub.globalOffset); + for (unsigned i = h.size() - 1; i >= 1; i--) { + // Array sizes must be constant or at least arithmetically + // evaluable at compile time + if (!isPureArithmetic(h[i])) + err("Array size must be fixed value", m); + // Create a list of the coefficient associated with each + // array index + coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); + } + pre.offsets = sub.offsets; + pre.coefficients = sub.coefficients; + pre.nonfinal = sub.nonfinal; + pre.nonfinal[prefix+h[0].val.substr(2)] = true; + } + pre.coefficients[prefix+h[0].val.substr(2)] = coefficients; + pre.offsets[prefix+h[0].val.substr(2)] = pre.globalOffset; + pre.indices[prefix+h[0].val.substr(2)] = index; + if (decimalGt(tt176, coefficients.back())) + pre.globalOffset = decimalAdd(pre.globalOffset, coefficients.back()); + return pre; +} + +// Preprocess input containing functions +// +// localExterns is a map of the form, eg, +// +// { x: { foo: 0, bar: 1, baz: 2 }, y: { qux: 0, foo: 1 } ... } +// +// localExternSigs is a map of the form, eg, +// +// { x : { foo: iii, bar: iis, baz: ia }, y: { qux: i, foo: as } ... } +// +// Signifying that x.foo = 0, x.baz = 2, y.foo = 1, etc +// and that x.foo has three integers as arguments, x.bar has two +// integers and a variable-length string, and baz has an integer +// and an array +// +// globalExterns is a one-level map, eg from above +// +// { foo: 1, bar: 1, baz: 2, qux: 0 } +// +// globalExternSigs is a one-level map, eg from above +// +// { foo: as, bar: iis, baz: ia, qux: i} +// +// Note that globalExterns and globalExternSigs may be ambiguous +// Also, a null signature implies an infinite tail of integers +preprocessResult preprocessInit(Node inp) { + Metadata m = inp.metadata; + if (inp.val != "seq") + inp = astnode("seq", inp, m); + std::vector empty = std::vector(); + Node init = astnode("seq", empty, m); + Node shared = astnode("seq", empty, m); + std::vector any; + std::vector functions; + preprocessAux out = preprocessAux(); + out.localExterns["self"] = std::map(); + int functionCount = 0; + int storageDataCount = 0; + for (unsigned i = 0; i < inp.args.size(); i++) { + Node obj = inp.args[i]; + // Functions + if (obj.val == "def") { + if (obj.args.size() == 0) + err("Empty def", m); + std::string funName = obj.args[0].val; + // Init, shared and any are special functions + if (funName == "init" || funName == "shared" || funName == "any") { + if (obj.args[0].args.size()) + err(funName+" cannot have arguments", m); + } + if (funName == "init") init = obj.args[1]; + else if (funName == "shared") shared = obj.args[1]; + else if (funName == "any") any.push_back(obj.args[1]); + else { + // Other functions + functions.push_back(convFunction(obj, functionCount)); + out.localExterns["self"][obj.args[0].val] = functionCount; + out.localExternSigs["self"][obj.args[0].val] + = getSignature(obj.args[0].args); + functionCount++; + } + } + // Extern declarations + else if (obj.val == "extern") { + std::string externName = obj.args[0].val; + Node al = obj.args[1]; + if (!out.localExterns.count(externName)) + out.localExterns[externName] = std::map(); + for (unsigned i = 0; i < al.args.size(); i++) { + if (al.args[i].val == ":") { + std::string v = al.args[i].args[0].val; + std::string sig = al.args[i].args[1].val; + out.globalExterns[v] = i; + out.globalExternSigs[v] = sig; + out.localExterns[externName][v] = i; + out.localExternSigs[externName][v] = sig; + } + else { + std::string v = al.args[i].val; + out.globalExterns[v] = i; + out.globalExternSigs[v] = ""; + out.localExterns[externName][v] = i; + out.localExternSigs[externName][v] = ""; + } + } + } + // Custom macros + else if (obj.val == "macro") { + // Rules for valid macros: + // + // There are only four categories of valid macros: + // + // 1. a macro where the outer function is something + // which is NOT an existing valid function/extern/datum + // 2. a macro of the form set(c(x), d) where c must NOT + // be an existing valid function/extern/datum + // 3. something of the form access(c(x)), where c must NOT + // be an existing valid function/extern/datum + // 4. something of the form set(access(c(x)), d) where c must + // NOT be an existing valid function/extern/datum + bool valid = false; + Node pattern = obj.args[0]; + Node substitution = obj.args[1]; + if (opcode(pattern.val) < 0 && !isValidFunctionName(pattern.val)) + valid = true; + if (pattern.val == "set" && + opcode(pattern.args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].val)) + valid = true; + if (pattern.val == "access" && + opcode(pattern.args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].val)) + if (pattern.val == "set" && + pattern.args[0].val == "access" && + opcode(pattern.args[0].args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].args[0].val)) + valid = true; + if (valid) { + out.customMacros.push_back(rewriteRule(pattern, substitution)); + } + } + // Variable types + else if (obj.val == "type") { + std::string typeName = obj.args[0].val; + std::vector vars = obj.args[1].args; + for (unsigned i = 0; i < vars.size(); i++) + out.types[vars[i].val] = typeName; + } + // Storage variables/structures + else if (obj.val == "data") { + out.storageVars = getStorageVars(out.storageVars, + obj.args[0], + "", + storageDataCount); + storageDataCount += 1; + } + else any.push_back(obj); + } + std::vector main; + if (shared.args.size()) main.push_back(shared); + if (init.args.size()) main.push_back(init); + + std::vector code; + if (shared.args.size()) code.push_back(shared); + for (unsigned i = 0; i < any.size(); i++) + code.push_back(any[i]); + for (unsigned i = 0; i < functions.size(); i++) + code.push_back(functions[i]); + Node codeNode; + if (functions.size() > 0) { + codeNode = astnode("with", + token("__funid", m), + astnode("byte", + token("0", m), + astnode("calldataload", token("0", m), m), + m), + astnode("seq", code, m), + m); + } + else codeNode = astnode("seq", code, m); + main.push_back(astnode("~return", + token("0", m), + astnode("lll", + codeNode, + token("0", m), + m), + m)); + + + Node result; + if (main.size() == 1) result = main[0]; + else result = astnode("seq", main, inp.metadata); + return preprocessResult(result, out); +} + +preprocessResult processTypes (preprocessResult pr) { + preprocessAux aux = pr.second; + Node node = pr.first; + if (node.type == TOKEN && aux.types.count(node.val)) { + node = asn(aux.types[node.val], node, node.metadata); + } + else if (node.val == "untyped") + return preprocessResult(node.args[0], aux); + else { + for (unsigned i = 0; i < node.args.size(); i++) { + node.args[i] = + processTypes(preprocessResult(node.args[i], aux)).first; + } + } + return preprocessResult(node, aux); +} + +preprocessResult preprocess(Node n) { + return processTypes(preprocessInit(n)); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.h new file mode 100644 index 000000000..944436aef --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/preprocess.h @@ -0,0 +1,58 @@ +#ifndef ETHSERP_PREPROCESSOR +#define ETHSERP_PREPROCESSOR + +#include +#include +#include +#include +#include "util.h" + +// Storage variable index storing object +struct svObj { + std::map offsets; + std::map indices; + std::map > coefficients; + std::map nonfinal; + std::string globalOffset; +}; + +class rewriteRule { + public: + rewriteRule(Node p, Node s) { + pattern = p; + substitution = s; + } + Node pattern; + Node substitution; +}; + + +// Preprocessing result storing object +class preprocessAux { + public: + preprocessAux() { + globalExterns = std::map(); + localExterns = std::map >(); + localExterns["self"] = std::map(); + } + std::map globalExterns; + std::map globalExternSigs; + std::map > localExterns; + std::map > localExternSigs; + std::vector customMacros; + std::map types; + svObj storageVars; +}; + +#define preprocessResult std::pair + +// Populate an svObj with the arguments needed to determine +// the storage position of a node +svObj getStorageVars(svObj pre, Node node, std::string prefix="", + int index=0); + +// Preprocess a function (see cpp for details) +preprocessResult preprocess(Node inp); + + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.cpp new file mode 100644 index 000000000..38398aa46 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.cpp @@ -0,0 +1,173 @@ +#include +#include "structmember.h" + +#include +#include +#include +#include "funcs.h" + +#define PYMETHOD(name, FROM, method, TO) \ + static PyObject * name(PyObject *, PyObject *args) { \ + try { \ + FROM(med) \ + return TO(method(med)); \ + } \ + catch (std::string e) { \ + PyErr_SetString(PyExc_Exception, e.c_str()); \ + return NULL; \ + } \ + } + +#define FROMSTR(v) \ + const char *command; \ + int len; \ + if (!PyArg_ParseTuple(args, "s#", &command, &len)) \ + return NULL; \ + std::string v = std::string(command, len); \ + +#define FROMNODE(v) \ + PyObject *node; \ + if (!PyArg_ParseTuple(args, "O", &node)) \ + return NULL; \ + Node v = cppifyNode(node); + +#define FROMLIST(v) \ + PyObject *node; \ + if (!PyArg_ParseTuple(args, "O", &node)) \ + return NULL; \ + std::vector v = cppifyNodeList(node); + +// Convert metadata into python wrapper form [file, ln, ch] +PyObject* pyifyMetadata(Metadata m) { + PyObject* a = PyList_New(0); + PyList_Append(a, Py_BuildValue("s#", m.file.c_str(), m.file.length())); + PyList_Append(a, Py_BuildValue("i", m.ln)); + PyList_Append(a, Py_BuildValue("i", m.ch)); + return a; +} + +// Convert node into python wrapper form +// [token=0/astnode=1, val, metadata, args] +PyObject* pyifyNode(Node n) { + PyObject* a = PyList_New(0); + PyList_Append(a, Py_BuildValue("i", n.type == ASTNODE)); + PyList_Append(a, Py_BuildValue("s#", n.val.c_str(), n.val.length())); + PyList_Append(a, pyifyMetadata(n.metadata)); + for (unsigned i = 0; i < n.args.size(); i++) + PyList_Append(a, pyifyNode(n.args[i])); + return a; +} + +// Convert string into python wrapper form +PyObject* pyifyString(std::string s) { + return Py_BuildValue("s#", s.c_str(), s.length()); +} + +// Convert list of nodes into python wrapper form +PyObject* pyifyNodeList(std::vector n) { + PyObject* a = PyList_New(0); + for (unsigned i = 0; i < n.size(); i++) + PyList_Append(a, pyifyNode(n[i])); + return a; +} + +// Convert pyobject int into normal form +int cppifyInt(PyObject* o) { + int out; + if (!PyArg_Parse(o, "i", &out)) + err("Argument should be integer", Metadata()); + return out; +} + +// Convert pyobject string into normal form +std::string cppifyString(PyObject* o) { + const char *command; + if (!PyArg_Parse(o, "s", &command)) + err("Argument should be string", Metadata()); + return std::string(command); +} + +// Convert metadata from python wrapper form +Metadata cppifyMetadata(PyObject* o) { + std::string file = cppifyString(PyList_GetItem(o, 0)); + int ln = cppifyInt(PyList_GetItem(o, 1)); + int ch = cppifyInt(PyList_GetItem(o, 2)); + return Metadata(file, ln, ch); +} + +// Convert node from python wrapper form +Node cppifyNode(PyObject* o) { + Node n; + int isAstNode = cppifyInt(PyList_GetItem(o, 0)); + n.type = isAstNode ? ASTNODE : TOKEN; + n.val = cppifyString(PyList_GetItem(o, 1)); + n.metadata = cppifyMetadata(PyList_GetItem(o, 2)); + std::vector args; + for (int i = 3; i < PyList_Size(o); i++) { + args.push_back(cppifyNode(PyList_GetItem(o, i))); + } + n.args = args; + return n; +} + +//Convert list of nodes into normal form +std::vector cppifyNodeList(PyObject* o) { + std::vector out; + for (int i = 0; i < PyList_Size(o); i++) { + out.push_back(cppifyNode(PyList_GetItem(o,i))); + } + return out; +} + +PYMETHOD(ps_compile, FROMSTR, compile, pyifyString) +PYMETHOD(ps_compile_chunk, FROMSTR, compileChunk, pyifyString) +PYMETHOD(ps_compile_to_lll, FROMSTR, compileToLLL, pyifyNode) +PYMETHOD(ps_compile_chunk_to_lll, FROMSTR, compileChunkToLLL, pyifyNode) +PYMETHOD(ps_compile_lll, FROMNODE, compileLLL, pyifyString) +PYMETHOD(ps_parse, FROMSTR, parseSerpent, pyifyNode) +PYMETHOD(ps_rewrite, FROMNODE, rewrite, pyifyNode) +PYMETHOD(ps_rewrite_chunk, FROMNODE, rewriteChunk, pyifyNode) +PYMETHOD(ps_pretty_compile, FROMSTR, prettyCompile, pyifyNodeList) +PYMETHOD(ps_pretty_compile_chunk, FROMSTR, prettyCompileChunk, pyifyNodeList) +PYMETHOD(ps_pretty_compile_lll, FROMNODE, prettyCompileLLL, pyifyNodeList) +PYMETHOD(ps_serialize, FROMLIST, serialize, pyifyString) +PYMETHOD(ps_deserialize, FROMSTR, deserialize, pyifyNodeList) +PYMETHOD(ps_parse_lll, FROMSTR, parseLLL, pyifyNode) + + +static PyMethodDef PyextMethods[] = { + {"compile", ps_compile, METH_VARARGS, + "Compile code."}, + {"compile_chunk", ps_compile_chunk, METH_VARARGS, + "Compile code chunk (no wrappers)."}, + {"compile_to_lll", ps_compile_to_lll, METH_VARARGS, + "Compile code to LLL."}, + {"compile_chunk_to_lll", ps_compile_chunk_to_lll, METH_VARARGS, + "Compile code chunk to LLL (no wrappers)."}, + {"compile_lll", ps_compile_lll, METH_VARARGS, + "Compile LLL to EVM."}, + {"parse", ps_parse, METH_VARARGS, + "Parse serpent"}, + {"rewrite", ps_rewrite, METH_VARARGS, + "Rewrite parsed serpent to LLL"}, + {"rewrite_chunk", ps_rewrite_chunk, METH_VARARGS, + "Rewrite parsed serpent to LLL (no wrappers)"}, + {"pretty_compile", ps_pretty_compile, METH_VARARGS, + "Compile to EVM opcodes"}, + {"pretty_compile_chunk", ps_pretty_compile_chunk, METH_VARARGS, + "Compile chunk to EVM opcodes (no wrappers)"}, + {"pretty_compile_lll", ps_pretty_compile_lll, METH_VARARGS, + "Compile LLL to EVM opcodes"}, + {"serialize", ps_serialize, METH_VARARGS, + "Convert EVM opcodes to bin"}, + {"deserialize", ps_deserialize, METH_VARARGS, + "Convert EVM bin to opcodes"}, + {"parse_lll", ps_parse_lll, METH_VARARGS, + "Parse LLL"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC initserpent_pyext(void) +{ + Py_InitModule( "serpent_pyext", PyextMethods ); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.py new file mode 100644 index 000000000..2103b48fe --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/pyserpent.py @@ -0,0 +1 @@ +from serpent import * diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.cpp new file mode 100644 index 000000000..4cdce4f0a --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.cpp @@ -0,0 +1,804 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" +#include "functions.h" +#include "opcodes.h" + +// Rewrite rules +std::string macros[][2] = { + { + "(seq $x)", + "$x" + }, + { + "(seq (seq) $x)", + "$x" + }, + { + "(+= $a $b)", + "(set $a (+ $a $b))" + }, + { + "(*= $a $b)", + "(set $a (* $a $b))" + }, + { + "(-= $a $b)", + "(set $a (- $a $b))" + }, + { + "(/= $a $b)", + "(set $a (/ $a $b))" + }, + { + "(%= $a $b)", + "(set $a (% $a $b))" + }, + { + "(^= $a $b)", + "(set $a (^ $a $b))" + }, + { + "(!= $a $b)", + "(iszero (eq $a $b))" + }, + { + "(assert $x)", + "(unless $x (stop))" + }, + { + "(min $a $b)", + "(with $1 $a (with $2 $b (if (lt $1 $2) $1 $2)))" + }, + { + "(max $a $b)", + "(with $1 $a (with $2 $b (if (lt $1 $2) $2 $1)))" + }, + { + "(smin $a $b)", + "(with $1 $a (with $2 $b (if (slt $1 $2) $1 $2)))" + }, + { + "(smax $a $b)", + "(with $1 $a (with $2 $b (if (slt $1 $2) $2 $1)))" + }, + { + "(if $cond $do (else $else))", + "(if $cond $do $else)" + }, + { + "(code $code)", + "$code" + }, + { + "(slice $arr $pos)", + "(add $arr (mul 32 $pos))", + }, + { + "(array $len)", + "(alloc (mul 32 $len))" + }, + { + "(while $cond $do)", + "(until (iszero $cond) $do)", + }, + { + "(while (iszero $cond) $do)", + "(until $cond $do)", + }, + { + "(if $cond $do)", + "(unless (iszero $cond) $do)", + }, + { + "(if (iszero $cond) $do)", + "(unless $cond $do)", + }, + { + "(access (. self storage) $ind)", + "(sload $ind)" + }, + { + "(access $var $ind)", + "(mload (add $var (mul 32 $ind)))" + }, + { + "(set (access (. self storage) $ind) $val)", + "(sstore $ind $val)" + }, + { + "(set (access $var $ind) $val)", + "(mstore (add $var (mul 32 $ind)) $val)" + }, + { + "(getch $var $ind)", + "(mod (mload (sub (add $var $ind) 31)) 256)" + }, + { + "(setch $var $ind $val)", + "(mstore8 (add $var $ind) $val)", + }, + { + "(send $to $value)", + "(~call (sub (gas) 25) $to $value 0 0 0 0)" + }, + { + "(send $gas $to $value)", + "(~call $gas $to $value 0 0 0 0)" + }, + { + "(sha3 $x)", + "(seq (set $1 $x) (~sha3 (ref $1) 32))" + }, + { + "(sha3 $mstart (= chars $msize))", + "(~sha3 $mstart $msize)" + }, + { + "(sha3 $mstart $msize)", + "(~sha3 $mstart (mul 32 $msize))" + }, + { + "(id $0)", + "$0" + }, + { + "(return $x)", + "(seq (set $1 $x) (~return (ref $1) 32))" + }, + { + "(return $mstart (= chars $msize))", + "(~return $mstart $msize)" + }, + { + "(return $start $len)", + "(~return $start (mul 32 $len))" + }, + { + "(&& $x $y)", + "(if $x $y 0)" + }, + { + "(|| $x $y)", + "(with $1 $x (if $1 $1 $y))" + }, + { + "(>= $x $y)", + "(iszero (slt $x $y))" + }, + { + "(<= $x $y)", + "(iszero (sgt $x $y))" + }, + { + "(create $code)", + "(create 0 $code)" + }, + { + "(create $endowment $code)", + "(with $1 (msize) (create $endowment (get $1) (lll (outer $code) (msize))))" + }, + { + "(sha256 $x)", + "(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 2 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" + }, + { + "(sha256 $arr (= chars $sz))", + "(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr $sz (get $1) 32)) (mload (get $1))))" + }, + { + "(sha256 $arr $sz)", + "(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" + }, + { + "(ripemd160 $x)", + "(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 3 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" + }, + { + "(ripemd160 $arr (= chars $sz))", + "(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr $sz (mload $1) 32)) (mload (get $1))))" + }, + { + "(ripemd160 $arr $sz)", + "(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" + }, + { + "(ecrecover $h $v $r $s)", + "(with $1 (alloc 160) (seq (mstore (get $1) $h) (mstore (add (get $1) 32) $v) (mstore (add (get $1) 64) $r) (mstore (add (get $1) 96) $s) (pop (~call 101 1 0 (get $1) 128 (add (get $1 128)) 32)) (mload (add (get $1) 128))))" + }, + { + "(inset $x)", + "$x" + }, + { + "(create $x)", + "(with $1 (msize) (create $val (get $1) (lll $code (get $1))))" + }, + { + "(with (= $var $val) $cond)", + "(with $var $val $cond)" + }, + { + "(log $t1)", + "(~log1 0 0 $t1)" + }, + { + "(log $t1 $t2)", + "(~log2 0 0 $t1 $t2)" + }, + { + "(log $t1 $t2 $t3)", + "(~log3 0 0 $t1 $t2 $t3)" + }, + { + "(log $t1 $t2 $t3 $t4)", + "(~log4 0 0 $t1 $t2 $t3 $t4)" + }, + { + "(logarr $a $sz)", + "(~log0 $a (mul 32 $sz))" + }, + { + "(logarr $a $sz $t1)", + "(~log1 $a (mul 32 $sz) $t1)" + }, + { + "(logarr $a $sz $t1 $t2)", + "(~log2 $a (mul 32 $sz) $t1 $t2)" + }, + { + "(logarr $a $sz $t1 $t2 $t3)", + "(~log3 $a (mul 32 $sz) $t1 $t2 $t3)" + }, + { + "(logarr $a $sz $t1 $t2 $t3 $t4)", + "(~log4 $a (mul 32 $sz) $t1 $t2 $t3 $t4)" + }, + { + "(save $loc $array (= chars $count))", + "(with $location (ref $loc) (with $c $count (with $end (div $c 32) (with $i 0 (seq (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))) (sstore (add $i $location) (~and (access $array $i) (sub 0 (exp 256 (sub 32 (mod $c 32)))))))))))" + }, + { + "(save $loc $array $count)", + "(with $location (ref $loc) (with $end $count (with $i 0 (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))))))" + }, + { + "(load $loc (= chars $count))", + "(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i (div $c 32)) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) (set (access $a $i) (~and (sload (add $location $i)) (sub 0 (exp 256 (sub 32 (mod $c 32)))))) $a)))))" + }, + { + "(load $loc $count)", + "(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i $c) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) $a)))))" + }, + { + "(unsafe_mcopy $to $from $sz)", + "(with _sz $sz (with _from $from (with _to $to (seq (comment STARTING UNSAFE MCOPY) (with _i 0 (while (lt _i _sz) (seq (mstore (add $to _i) (mload (add _from _i))) (set _i (add _i 32)))))))))" + }, + { + "(mcopy $to $from $_sz)", + "(with _to $to (with _from $from (with _sz $sz (seq (comment STARTING MCOPY (with _i 0 (seq (while (lt (add _i 31) _sz) (seq (mstore (add _to _i) (mload (add _from _i))) (set _i (add _i 32)))) (with _mask (exp 256 (sub 32 (mod _sz 32))) (mstore (add $to _i) (add (mod (mload (add $to _i)) _mask) (and (mload (add $from _i)) (sub 0 _mask))))))))))))" + }, + { "(. msg sender)", "(caller)" }, + { "(. msg value)", "(callvalue)" }, + { "(. tx gasprice)", "(gasprice)" }, + { "(. tx origin)", "(origin)" }, + { "(. tx gas)", "(gas)" }, + { "(. $x balance)", "(balance $x)" }, + { "self", "(address)" }, + { "(. block prevhash)", "(prevhash)" }, + { "(. block coinbase)", "(coinbase)" }, + { "(. block timestamp)", "(timestamp)" }, + { "(. block number)", "(number)" }, + { "(. block difficulty)", "(difficulty)" }, + { "(. block gaslimit)", "(gaslimit)" }, + { "stop", "(stop)" }, + { "---END---", "" } //Keep this line at the end of the list +}; + +std::vector nodeMacros; + +// Token synonyms +std::string synonyms[][2] = { + { "or", "||" }, + { "and", "&&" }, + { "|", "~or" }, + { "&", "~and" }, + { "elif", "if" }, + { "!", "iszero" }, + { "~", "~not" }, + { "not", "iszero" }, + { "string", "alloc" }, + { "+", "add" }, + { "-", "sub" }, + { "*", "mul" }, + { "/", "sdiv" }, + { "^", "exp" }, + { "**", "exp" }, + { "%", "smod" }, + { "<", "slt" }, + { ">", "sgt" }, + { "=", "set" }, + { "==", "eq" }, + { ":", "kv" }, + { "---END---", "" } //Keep this line at the end of the list +}; + +// Custom setters (need to be registered separately +// for use with managed storage) +std::string setters[][2] = { + { "+=", "+" }, + { "-=", "-" }, + { "*=", "*" }, + { "/=", "/" }, + { "%=", "%" }, + { "^=", "^" }, + { "---END---", "" } //Keep this line at the end of the list +}; + +// Processes mutable array literals +Node array_lit_transform(Node node) { + std::string prefix = "_temp"+mkUniqueToken() + "_"; + Metadata m = node.metadata; + std::map d; + std::string o = "(seq (set $arr (alloc "+utd(node.args.size()*32)+"))"; + for (unsigned i = 0; i < node.args.size(); i++) { + o += " (mstore (add (get $arr) "+utd(i * 32)+") $"+utd(i)+")"; + d[utd(i)] = node.args[i]; + } + o += " (get $arr))"; + return subst(parseLLL(o), d, prefix, m); +} + + +Node apply_rules(preprocessResult pr); + +// Transform ".(args...)" into +// a call +Node dotTransform(Node node, preprocessAux aux) { + Metadata m = node.metadata; + // We're gonna make lots of temporary variables, + // so set up a unique flag for them + std::string prefix = "_temp"+mkUniqueToken()+"_"; + // Check that the function name is a token + if (node.args[0].args[1].type == ASTNODE) + err("Function name must be static", m); + + Node dotOwner = node.args[0].args[0]; + std::string dotMember = node.args[0].args[1].val; + // kwargs = map of special arguments + std::map kwargs; + kwargs["value"] = token("0", m); + kwargs["gas"] = subst(parseLLL("(- (gas) 25)"), msn(), prefix, m); + // Search for as=? and call=code keywords, and isolate the actual + // function arguments + std::vector fnargs; + std::string as = ""; + std::string op = "call"; + for (unsigned i = 1; i < node.args.size(); i++) { + fnargs.push_back(node.args[i]); + Node arg = fnargs.back(); + if (arg.val == "=" || arg.val == "set") { + if (arg.args[0].val == "as") + as = arg.args[1].val; + if (arg.args[0].val == "call" && arg.args[1].val == "code") + op = "callcode"; + if (arg.args[0].val == "gas") + kwargs["gas"] = arg.args[1]; + if (arg.args[0].val == "value") + kwargs["value"] = arg.args[1]; + if (arg.args[0].val == "outsz") + kwargs["outsz"] = arg.args[1]; + } + } + if (dotOwner.val == "self") { + if (as.size()) err("Cannot use \"as\" when calling self!", m); + as = dotOwner.val; + } + // Determine the funId and sig assuming the "as" keyword was used + int funId = 0; + std::string sig; + if (as.size() > 0 && aux.localExterns.count(as)) { + if (!aux.localExterns[as].count(dotMember)) + err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + funId = aux.localExterns[as][dotMember]; + sig = aux.localExternSigs[as][dotMember]; + } + // Determine the funId and sig otherwise + else if (!as.size()) { + if (!aux.globalExterns.count(dotMember)) + err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + std::string key = unsignedToDecimal(aux.globalExterns[dotMember]); + funId = aux.globalExterns[dotMember]; + sig = aux.globalExternSigs[dotMember]; + } + else err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + // Pack arguments + kwargs["data"] = packArguments(fnargs, sig, funId, m); + kwargs["to"] = dotOwner; + Node main; + // Pack output + if (!kwargs.count("outsz")) { + main = parseLLL( + "(with _data $data (seq " + "(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) (ref $dataout) 32))" + "(get $dataout)))"); + } + else { + main = parseLLL( + "(with _data $data (with _outsz (mul 32 $outsz) (with _out (alloc _outsz) (seq " + "(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) _out _outsz))" + "(get _out)))))"); + } + // Set up main call + + Node o = subst(main, kwargs, prefix, m); + return o; +} + +// Transform an access of the form self.bob, self.users[5], etc into +// a storage access +// +// There exist two types of objects: finite objects, and infinite +// objects. Finite objects are packed optimally tightly into storage +// accesses; for example: +// +// data obj[100](a, b[2][4], c) +// +// obj[0].a -> 0 +// obj[0].b[0][0] -> 1 +// obj[0].b[1][3] -> 8 +// obj[45].c -> 459 +// +// Infinite objects are accessed by sha3([v1, v2, v3 ... ]), where +// the values are a list of array indices and keyword indices, for +// example: +// data obj[](a, b[2][4], c) +// data obj2[](a, b[][], c) +// +// obj[0].a -> sha3([0, 0, 0]) +// obj[5].b[1][3] -> sha3([0, 5, 1, 1, 3]) +// obj[45].c -> sha3([0, 45, 2]) +// obj2[0].a -> sha3([1, 0, 0]) +// obj2[5].b[1][3] -> sha3([1, 5, 1, 1, 3]) +// obj2[45].c -> sha3([1, 45, 2]) +Node storageTransform(Node node, preprocessAux aux, + bool mapstyle=false, bool ref=false) { + Metadata m = node.metadata; + // Get a list of all of the "access parameters" used in order + // eg. self.users[5].cow[4][m[2]][woof] -> + // [--self, --users, 5, --cow, 4, m[2], woof] + std::vector hlist = listfyStorageAccess(node); + // For infinite arrays, the terms array will just provide a list + // of indices. For finite arrays, it's a list of index*coefficient + std::vector terms; + std::string offset = "0"; + std::string prefix = ""; + std::string varPrefix = "_temp"+mkUniqueToken()+"_"; + int c = 0; + std::vector coefficients; + coefficients.push_back(""); + for (unsigned i = 1; i < hlist.size(); i++) { + // We pre-add the -- flag to parameter-like terms. For example, + // self.users[m] -> [--self, --users, m] + // self.users.m -> [--self, --users, --m] + if (hlist[i].val.substr(0, 2) == "--") { + prefix += hlist[i].val.substr(2) + "."; + std::string tempPrefix = prefix.substr(0, prefix.size()-1); + if (!aux.storageVars.offsets.count(tempPrefix)) + return node; + if (c < (signed)coefficients.size() - 1) + err("Too few array index lookups", m); + if (c > (signed)coefficients.size() - 1) + err("Too many array index lookups", m); + coefficients = aux.storageVars.coefficients[tempPrefix]; + // If the size of an object exceeds 2^176, we make it an infinite + // array + if (decimalGt(coefficients.back(), tt176) && !mapstyle) + return storageTransform(node, aux, true, ref); + offset = decimalAdd(offset, aux.storageVars.offsets[tempPrefix]); + c = 0; + if (mapstyle) + terms.push_back(token(unsignedToDecimal( + aux.storageVars.indices[tempPrefix]))); + } + else if (mapstyle) { + terms.push_back(hlist[i]); + c += 1; + } + else { + if (c > (signed)coefficients.size() - 2) + err("Too many array index lookups", m); + terms.push_back( + astnode("mul", + hlist[i], + token(coefficients[coefficients.size() - 2 - c], m), + m)); + + c += 1; + } + } + if (aux.storageVars.nonfinal.count(prefix.substr(0, prefix.size()-1))) + err("Storage variable access not deep enough", m); + if (c < (signed)coefficients.size() - 1) { + err("Too few array index lookups", m); + } + if (c > (signed)coefficients.size() - 1) { + err("Too many array index lookups", m); + } + Node o; + if (mapstyle) { + std::string t = "_temp_"+mkUniqueToken(); + std::vector sub; + for (unsigned i = 0; i < terms.size(); i++) + sub.push_back(asn("mstore", + asn("add", + tkn(utd(i * 32), m), + asn("get", tkn(t+"pos", m), m), + m), + terms[i], + m)); + sub.push_back(tkn(t+"pos", m)); + Node main = asn("with", + tkn(t+"pos", m), + asn("alloc", tkn(utd(terms.size() * 32), m), m), + asn("seq", sub, m), + m); + Node sz = token(utd(terms.size() * 32), m); + o = astnode("~sha3", + main, + sz, + m); + } + else { + // We add up all the index*coefficients + Node out = token(offset, node.metadata); + for (unsigned i = 0; i < terms.size(); i++) { + std::vector temp; + temp.push_back(out); + temp.push_back(terms[i]); + out = astnode("add", temp, node.metadata); + } + o = out; + } + if (ref) return o; + else return astnode("sload", o, node.metadata); +} + + +// Recursively applies rewrite rules +std::pair apply_rules_iter(preprocessResult pr) { + bool changed = false; + Node node = pr.first; + // If the rewrite rules have not yet been parsed, parse them + if (!nodeMacros.size()) { + for (int i = 0; i < 9999; i++) { + std::vector o; + if (macros[i][0] == "---END---") break; + nodeMacros.push_back(rewriteRule( + parseLLL(macros[i][0]), + parseLLL(macros[i][1]) + )); + } + } + // Assignment transformations + for (int i = 0; i < 9999; i++) { + if (setters[i][0] == "---END---") break; + if (node.val == setters[i][0]) { + node = astnode("=", + node.args[0], + astnode(setters[i][1], + node.args[0], + node.args[1], + node.metadata), + node.metadata); + } + } + // Do nothing to macros + if (node.val == "macro") { + return std::pair(node, changed); + } + // Ignore comments + if (node.val == "comment") { + return std::pair(node, changed); + } + // Special storage transformation + if (isNodeStorageVariable(node)) { + node = storageTransform(node, pr.second); + changed = true; + } + if (node.val == "ref" && isNodeStorageVariable(node.args[0])) { + node = storageTransform(node.args[0], pr.second, false, true); + changed = true; + } + if (node.val == "=" && isNodeStorageVariable(node.args[0])) { + Node t = storageTransform(node.args[0], pr.second); + if (t.val == "sload") { + std::vector o; + o.push_back(t.args[0]); + o.push_back(node.args[1]); + node = astnode("sstore", o, node.metadata); + } + changed = true; + } + // Main code + unsigned pos = 0; + std::string prefix = "_temp"+mkUniqueToken()+"_"; + while(1) { + if (synonyms[pos][0] == "---END---") { + break; + } + else if (node.type == ASTNODE && node.val == synonyms[pos][0]) { + node.val = synonyms[pos][1]; + changed = true; + } + pos++; + } + for (pos = 0; pos < nodeMacros.size() + pr.second.customMacros.size(); pos++) { + rewriteRule macro = pos < nodeMacros.size() + ? nodeMacros[pos] + : pr.second.customMacros[pos - nodeMacros.size()]; + matchResult mr = match(macro.pattern, node); + if (mr.success) { + node = subst(macro.substitution, mr.map, prefix, node.metadata); + std::pair o = + apply_rules_iter(preprocessResult(node, pr.second)); + o.second = true; + return o; + } + } + // Special transformations + if (node.val == "outer") { + node = apply_rules(preprocess(node.args[0])); + changed = true; + } + if (node.val == "array_lit") { + node = array_lit_transform(node); + changed = true; + } + if (node.val == "fun" && node.args[0].val == ".") { + node = dotTransform(node, pr.second); + changed = true; + } + if (node.type == ASTNODE) { + unsigned i = 0; + if (node.val == "set" || node.val == "ref" + || node.val == "get" || node.val == "with") { + if (node.args[0].val.size() > 0 && node.args[0].val[0] != '\'' + && node.args[0].type == TOKEN && node.args[0].val[0] != '$') { + node.args[0].val = "'" + node.args[0].val; + changed = true; + } + i = 1; + } + else if (node.val == "arglen") { + node.val = "get"; + node.args[0].val = "'_len_" + node.args[0].val; + i = 1; + changed = true; + } + for (; i < node.args.size(); i++) { + std::pair r = + apply_rules_iter(preprocessResult(node.args[i], pr.second)); + node.args[i] = r.first; + changed = changed || r.second; + } + } + else if (node.type == TOKEN && !isNumberLike(node)) { + if (node.val.size() >= 2 + && node.val[0] == '"' + && node.val[node.val.size() - 1] == '"') { + std::string bin = node.val.substr(1, node.val.size() - 2); + unsigned sz = bin.size(); + std::vector o; + for (unsigned i = 0; i < sz; i += 32) { + std::string t = binToNumeric(bin.substr(i, 32)); + if ((sz - i) < 32 && (sz - i) > 0) { + while ((sz - i) < 32) { + t = decimalMul(t, "256"); + i--; + } + i = sz; + } + o.push_back(token(t, node.metadata)); + } + node = astnode("array_lit", o, node.metadata); + std::pair r = + apply_rules_iter(preprocessResult(node, pr.second)); + node = r.first; + changed = true; + } + else if (node.val.size() && node.val[0] != '\'' && node.val[0] != '$') { + node.val = "'" + node.val; + std::vector args; + args.push_back(node); + std::string v = node.val.substr(1); + node = astnode("get", args, node.metadata); + changed = true; + } + } + return std::pair(node, changed); +} + +Node apply_rules(preprocessResult pr) { + for (unsigned i = 0; i < pr.second.customMacros.size(); i++) { + pr.second.customMacros[i].pattern = + apply_rules(preprocessResult(pr.second.customMacros[i].pattern, preprocessAux())); + } + while (1) { + //std::cerr << printAST(pr.first) << + // " " << pr.second.customMacros.size() << "\n"; + std::pair r = apply_rules_iter(pr); + if (!r.second) { + return r.first; + } + pr.first = r.first; + } +} + +Node validate(Node inp) { + Metadata m = inp.metadata; + if (inp.type == ASTNODE) { + int i = 0; + while(validFunctions[i][0] != "---END---") { + if (inp.val == validFunctions[i][0]) { + std::string sz = unsignedToDecimal(inp.args.size()); + if (decimalGt(validFunctions[i][1], sz)) { + err("Too few arguments for "+inp.val, inp.metadata); + } + if (decimalGt(sz, validFunctions[i][2])) { + err("Too many arguments for "+inp.val, inp.metadata); + } + } + i++; + } + } + for (unsigned i = 0; i < inp.args.size(); i++) validate(inp.args[i]); + return inp; +} + +Node postValidate(Node inp) { + // This allows people to use ~x as a way of having functions with the same + // name and arity as macros; the idea is that ~x is a "final" form, and + // should not be remacroed, but it is converted back at the end + if (inp.val.size() > 0 && inp.val[0] == '~') { + inp.val = inp.val.substr(1); + } + if (inp.type == ASTNODE) { + if (inp.val == ".") + err("Invalid object member (ie. a foo.bar not mapped to anything)", + inp.metadata); + else if (opcode(inp.val) >= 0) { + if ((signed)inp.args.size() < opinputs(inp.val)) + err("Too few arguments for "+inp.val, inp.metadata); + if ((signed)inp.args.size() > opinputs(inp.val)) + err("Too many arguments for "+inp.val, inp.metadata); + } + else if (isValidLLLFunc(inp.val, inp.args.size())) { + // do nothing + } + else err ("Invalid argument count or LLL function: "+inp.val, inp.metadata); + for (unsigned i = 0; i < inp.args.size(); i++) { + inp.args[i] = postValidate(inp.args[i]); + } + } + return inp; +} + +Node rewrite(Node inp) { + return postValidate(optimize(apply_rules(preprocess(inp)))); +} + +Node rewriteChunk(Node inp) { + return postValidate(optimize(apply_rules( + preprocessResult( + validate(inp), preprocessAux())))); +} + +using namespace std; diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.h new file mode 100644 index 000000000..716815cee --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriter.h @@ -0,0 +1,16 @@ +#ifndef ETHSERP_REWRITER +#define ETHSERP_REWRITER + +#include +#include +#include +#include +#include "util.h" + +// Applies rewrite rules +Node rewrite(Node inp); + +// Applies rewrite rules adding without wrapper +Node rewriteChunk(Node inp); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.cpp new file mode 100644 index 000000000..0d810bdbc --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "rewriteutils.h" +#include "optimize.h" + +// Valid functions and their min and max argument counts +std::string validFunctions[][3] = { + { "if", "2", "3" }, + { "unless", "2", "2" }, + { "while", "2", "2" }, + { "until", "2", "2" }, + { "alloc", "1", "1" }, + { "array", "1", "1" }, + { "call", "2", tt256 }, + { "callcode", "2", tt256 }, + { "create", "1", "4" }, + { "getch", "2", "2" }, + { "setch", "3", "3" }, + { "sha3", "1", "2" }, + { "return", "1", "2" }, + { "inset", "1", "1" }, + { "min", "2", "2" }, + { "max", "2", "2" }, + { "array_lit", "0", tt256 }, + { "seq", "0", tt256 }, + { "log", "1", "6" }, + { "outer", "1", "1" }, + { "set", "2", "2" }, + { "get", "1", "1" }, + { "ref", "1", "1" }, + { "declare", "1", tt256 }, + { "with", "3", "3" }, + { "outer", "1", "1" }, + { "mcopy", "3", "3" }, + { "unsafe_mcopy", "3", "3" }, + { "save", "3", "3" }, + { "load", "2", "2" }, + { "---END---", "", "" } //Keep this line at the end of the list +}; + +std::map vfMap; + +// Is a function name one of the valid functions above? +bool isValidFunctionName(std::string f) { + if (vfMap.size() == 0) { + for (int i = 0; ; i++) { + if (validFunctions[i][0] == "---END---") break; + vfMap[validFunctions[i][0]] = true; + } + } + return vfMap.count(f); +} + +// Cool function for debug purposes (named cerrStringList to make +// all prints searchable via 'cerr') +void cerrStringList(std::vector s, std::string suffix) { + for (unsigned i = 0; i < s.size(); i++) std::cerr << s[i] << " "; + std::cerr << suffix << "\n"; +} + +// Convert: +// self.cow -> ["cow"] +// self.horse[0] -> ["horse", "0"] +// self.a[6][7][self.storage[3]].chicken[9] -> +// ["6", "7", (sload 3), "chicken", "9"] +std::vector listfyStorageAccess(Node node) { + std::vector out; + std::vector nodez; + nodez.push_back(node); + while (1) { + if (nodez.back().type == TOKEN) { + out.push_back(token("--" + nodez.back().val, node.metadata)); + std::vector outrev; + for (int i = (signed)out.size() - 1; i >= 0; i--) { + outrev.push_back(out[i]); + } + return outrev; + } + if (nodez.back().val == ".") + nodez.back().args[1].val = "--" + nodez.back().args[1].val; + if (nodez.back().args.size() == 0) + err("Error parsing storage variable statement", node.metadata); + if (nodez.back().args.size() == 1) + out.push_back(token(tt256m1, node.metadata)); + else + out.push_back(nodez.back().args[1]); + nodez.push_back(nodez.back().args[0]); + } +} + +// Is the given node something of the form +// self.cow +// self.horse[0] +// self.a[6][7][self.storage[3]].chicken[9] +bool isNodeStorageVariable(Node node) { + std::vector nodez; + nodez.push_back(node); + while (1) { + if (nodez.back().type == TOKEN) return false; + if (nodez.back().args.size() == 0) return false; + if (nodez.back().val != "." && nodez.back().val != "access") + return false; + if (nodez.back().args[0].val == "self") return true; + nodez.push_back(nodez.back().args[0]); + } +} + +// Main pattern matching routine, for those patterns that can be expressed +// using our standard mini-language above +// +// Returns two values. First, a boolean to determine whether the node matches +// the pattern, second, if the node does match then a map mapping variables +// in the pattern to nodes +matchResult match(Node p, Node n) { + matchResult o; + o.success = false; + if (p.type == TOKEN) { + if (p.val == n.val && n.type == TOKEN) o.success = true; + else if (p.val[0] == '$' || p.val[0] == '@') { + o.success = true; + o.map[p.val.substr(1)] = n; + } + } + else if (n.type==TOKEN || p.val!=n.val || p.args.size()!=n.args.size()) { + // do nothing + } + else { + for (unsigned i = 0; i < p.args.size(); i++) { + matchResult oPrime = match(p.args[i], n.args[i]); + if (!oPrime.success) { + o.success = false; + return o; + } + for (std::map::iterator it = oPrime.map.begin(); + it != oPrime.map.end(); + it++) { + o.map[(*it).first] = (*it).second; + } + } + o.success = true; + } + return o; +} + + +// Fills in the pattern with a dictionary mapping variable names to +// nodes (these dicts are generated by match). Match and subst together +// create a full pattern-matching engine. +Node subst(Node pattern, + std::map dict, + std::string varflag, + Metadata m) { + // Swap out patterns at the token level + if (pattern.metadata.ln == -1) + pattern.metadata = m; + if (pattern.type == TOKEN && + pattern.val[0] == '$') { + if (dict.count(pattern.val.substr(1))) { + return dict[pattern.val.substr(1)]; + } + else { + return token(varflag + pattern.val.substr(1), m); + } + } + // Other tokens are untouched + else if (pattern.type == TOKEN) { + return pattern; + } + // Substitute recursively for ASTs + else { + std::vector args; + for (unsigned i = 0; i < pattern.args.size(); i++) { + args.push_back(subst(pattern.args[i], dict, varflag, m)); + } + return asn(pattern.val, args, m); + } +} + +// Transforms a sequence containing two-argument with statements +// into a statement containing those statements in nested form +Node withTransform (Node source) { + Node o = token("--"); + Metadata m = source.metadata; + std::vector args; + for (int i = source.args.size() - 1; i >= 0; i--) { + Node a = source.args[i]; + if (a.val == "with" && a.args.size() == 2) { + std::vector flipargs; + for (int j = args.size() - 1; j >= 0; j--) + flipargs.push_back(args[i]); + if (o.val != "--") + flipargs.push_back(o); + o = asn("with", a.args[0], a.args[1], asn("seq", flipargs, m), m); + args = std::vector(); + } + else { + args.push_back(a); + } + } + std::vector flipargs; + for (int j = args.size() - 1; j >= 0; j--) + flipargs.push_back(args[j]); + if (o.val != "--") + flipargs.push_back(o); + return asn("seq", flipargs, m); +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.h new file mode 100644 index 000000000..8abf44a9f --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/rewriteutils.h @@ -0,0 +1,51 @@ +#ifndef ETHSERP_REWRITEUTILS +#define ETHSERP_REWRITEUTILS + +#include +#include +#include +#include +#include "util.h" + +// Valid functions and their min and max argument counts +extern std::string validFunctions[][3]; + +extern std::map vfMap; + +bool isValidFunctionName(std::string f); + +// Converts deep array access into ordered list of the arguments +// along the descent +std::vector listfyStorageAccess(Node node); + +// Cool function for debug purposes (named cerrStringList to make +// all prints searchable via 'cerr') +void cerrStringList(std::vector s, std::string suffix=""); + +// Is the given node something of the form +// self.cow +// self.horse[0] +// self.a[6][7][self.storage[3]].chicken[9] +bool isNodeStorageVariable(Node node); + +// Applies rewrite rules adding without wrapper +Node rewriteChunk(Node inp); + +// Match result storing object +struct matchResult { + bool success; + std::map map; +}; + +// Match node to pattern +matchResult match(Node p, Node n); + +// Substitute node using pattern +Node subst(Node pattern, + std::map dict, + std::string varflag, + Metadata m); + +Node withTransform(Node source); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/serpent.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/serpent.py new file mode 100644 index 000000000..8d6bedfe3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/serpent.py @@ -0,0 +1,201 @@ +import serpent_pyext as pyext +import sys +import re + +VERSION = '1.7.7' + + +class Metadata(object): + def __init__(self, li): + self.file = li[0] + self.ln = li[1] + self.ch = li[2] + + def out(self): + return [self.file, self.ln, self.ch] + + +class Token(object): + def __init__(self, val, metadata): + self.val = val + self.metadata = Metadata(metadata) + + def out(self): + return [0, self.val, self.metadata.out()] + + def __repr__(self): + return str(self.val) + + +class Astnode(object): + def __init__(self, val, args, metadata): + self.val = val + self.args = map(node, args) + self.metadata = Metadata(metadata) + + def out(self): + o = [1, self.val, self.metadata.out()]+[x.out() for x in self.args] + return o + + def __repr__(self): + o = '(' + self.val + subs = map(repr, self.args) + k = 0 + out = " " + while k < len(subs) and o != "(seq": + if '\n' in subs[k] or len(out + subs[k]) >= 80: + break + out += subs[k] + " " + k += 1 + if k < len(subs): + o += out + "\n " + o += '\n '.join('\n'.join(subs[k:]).split('\n')) + o += '\n)' + else: + o += out[:-1] + ')' + return o + + +def node(li): + if li[0]: + return Astnode(li[1], li[3:], li[2]) + else: + return Token(li[1], li[2]) + + +def take(x): + return pyext.parse_lll(x) if isinstance(x, (str, unicode)) else x.out() + + +def takelist(x): + return map(take, parse(x).args if isinstance(x, (str, unicode)) else x) + + +compile = lambda x: pyext.compile(x) +compile_chunk = lambda x: pyext.compile_chunk(x) +compile_to_lll = lambda x: node(pyext.compile_to_lll(x)) +compile_chunk_to_lll = lambda x: node(pyext.compile_chunk_to_lll(x)) +compile_lll = lambda x: pyext.compile_lll(take(x)) +parse = lambda x: node(pyext.parse(x)) +rewrite = lambda x: node(pyext.rewrite(take(x))) +rewrite_chunk = lambda x: node(pyext.rewrite_chunk(take(x))) +pretty_compile = lambda x: map(node, pyext.pretty_compile(x)) +pretty_compile_chunk = lambda x: map(node, pyext.pretty_compile_chunk(x)) +pretty_compile_lll = lambda x: map(node, pyext.pretty_compile_lll(take(x))) +serialize = lambda x: pyext.serialize(takelist(x)) +deserialize = lambda x: map(node, pyext.deserialize(x)) + +is_numeric = lambda x: isinstance(x, (int, long)) +is_string = lambda x: isinstance(x, (str, unicode)) +tobytearr = lambda n, L: [] if L == 0 else tobytearr(n / 256, L - 1)+[n % 256] + + +# A set of methods for detecting raw values (numbers and strings) and +# converting them to integers +def frombytes(b): + return 0 if len(b) == 0 else ord(b[-1]) + 256 * frombytes(b[:-1]) + + +def fromhex(b): + hexord = lambda x: '0123456789abcdef'.find(x) + return 0 if len(b) == 0 else hexord(b[-1]) + 16 * fromhex(b[:-1]) + + +def numberize(b): + if is_numeric(b): + return b + elif b[0] in ["'", '"']: + return frombytes(b[1:-1]) + elif b[:2] == '0x': + return fromhex(b[2:]) + elif re.match('^[0-9]*$', b): + return int(b) + elif len(b) == 40: + return fromhex(b) + else: + raise Exception("Cannot identify data type: %r" % b) + + +def enc(n): + if is_numeric(n): + return ''.join(map(chr, tobytearr(n, 32))) + elif is_string(n) and len(n) == 40: + return '\x00' * 12 + n.decode('hex') + elif is_string(n): + return '\x00' * (32 - len(n)) + n + elif n is True: + return 1 + elif n is False or n is None: + return 0 + + +def encode_datalist(*args): + if isinstance(args, (tuple, list)): + return ''.join(map(enc, args)) + elif not len(args) or args[0] == '': + return '' + else: + # Assume you're getting in numbers or addresses or 0x... + return ''.join(map(enc, map(numberize, args))) + + +def decode_datalist(arr): + if isinstance(arr, list): + arr = ''.join(map(chr, arr)) + o = [] + for i in range(0, len(arr), 32): + o.append(frombytes(arr[i:i + 32])) + return o + + +def encode_abi(funid, *args): + len_args = '' + normal_args = '' + var_args = '' + for arg in args: + if isinstance(arg, str) and len(arg) and \ + arg[0] == '"' and arg[-1] == '"': + len_args += enc(numberize(len(arg[1:-1]))) + var_args += arg[1:-1] + elif isinstance(arg, list): + for a in arg: + var_args += enc(numberize(a)) + len_args += enc(numberize(len(arg))) + else: + normal_args += enc(numberize(arg)) + return chr(int(funid)) + len_args + normal_args + var_args + + +def decode_abi(arr, *lens): + o = [] + pos = 1 + i = 0 + if len(lens) == 1 and isinstance(lens[0], list): + lens = lens[0] + while pos < len(arr): + bytez = int(lens[i]) if i < len(lens) else 32 + o.append(frombytes(arr[pos: pos + bytez])) + i, pos = i + 1, pos + bytez + return o + + +def main(): + if len(sys.argv) == 1: + print "serpent ..." + else: + cmd = sys.argv[2] if sys.argv[1] == '-s' else sys.argv[1] + if sys.argv[1] == '-s': + args = [sys.stdin.read()] + sys.argv[3:] + elif sys.argv[1] == '-v': + print VERSION + sys.exit() + else: + cmd = sys.argv[1] + args = sys.argv[2:] + if cmd in ['deserialize', 'decode_datalist', 'decode_abi']: + args[0] = args[0].strip().decode('hex') + o = globals()[cmd](*args) + if isinstance(o, (Token, Astnode, list)): + print repr(o) + else: + print o.encode('hex') diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/setup.py b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/setup.py new file mode 100644 index 000000000..5fdc1c16a --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/setup.py @@ -0,0 +1,46 @@ +from setuptools import setup, Extension + +import os +from distutils.sysconfig import get_config_vars + +(opt,) = get_config_vars('OPT') +os.environ['OPT'] = " ".join( + flag for flag in opt.split() if flag != '-Wstrict-prototypes' +) + +setup( + # Name of this package + name="ethereum-serpent", + + # Package version + version='1.7.7', + + description='Serpent compiler', + maintainer='Vitalik Buterin', + maintainer_email='v@buterin.com', + license='WTFPL', + url='http://www.ethereum.org/', + + # Describes how to build the actual extension module from C source files. + ext_modules=[ + Extension( + 'serpent_pyext', # Python name of the module + ['bignum.cpp', 'util.cpp', 'tokenize.cpp', + 'lllparser.cpp', 'parser.cpp', 'functions.cpp', + 'optimize.cpp', 'opcodes.cpp', + 'rewriteutils.cpp', 'preprocess.cpp', 'rewriter.cpp', + 'compiler.cpp', 'funcs.cpp', 'pyserpent.cpp'] + )], + py_modules=[ + 'serpent', + 'pyserpent' + ], + scripts=[ + 'serpent.py' + ], + entry_points={ + 'console_scripts': [ + 'serpent = serpent:main', + ], + } + ), diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.cpp new file mode 100644 index 000000000..b60cc8a44 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include "util.h" + +// These appear as independent tokens even if inside a stream of symbols +const std::string atoms[] = { "#", "//", "(", ")", "[", "]", "{", "}" }; +const int numAtoms = 8; + +// Is the char alphanumeric, a space, a bracket, a quote, a symbol? +int chartype(char c) { + if (c >= '0' && c <= '9') return ALPHANUM; + else if (c >= 'a' && c <= 'z') return ALPHANUM; + else if (c >= 'A' && c <= 'Z') return ALPHANUM; + else if (std::string("~_$@").find(c) != std::string::npos) return ALPHANUM; + else if (c == '\t' || c == ' ' || c == '\n' || c == '\r') return SPACE; + else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK; + else if (c == '"') return DQUOTE; + else if (c == '\'') return SQUOTE; + else return SYMB; +} + +// "y = f(45,124)/3" -> [ "y", "f", "(", "45", ",", "124", ")", "/", "3"] +std::vector tokenize(std::string inp, Metadata metadata, bool lispMode) { + int curtype = SPACE; + unsigned pos = 0; + int lastNewline = 0; + metadata.ch = 0; + std::string cur; + std::vector out; + + inp += " "; + while (pos < inp.length()) { + int headtype = chartype(inp[pos]); + if (lispMode) { + if (inp[pos] == '\'') headtype = ALPHANUM; + } + // Are we inside a quote? + if (curtype == SQUOTE || curtype == DQUOTE) { + // Close quote + if (headtype == curtype) { + cur += inp[pos]; + out.push_back(token(cur, metadata)); + cur = ""; + metadata.ch = pos - lastNewline; + curtype = SPACE; + pos += 1; + } + // eg. \xc3 + else if (inp.length() >= pos + 4 && inp.substr(pos, 2) == "\\x") { + cur += (std::string("0123456789abcdef").find(inp[pos+2]) * 16 + + std::string("0123456789abcdef").find(inp[pos+3])); + pos += 4; + } + // Newline + else if (inp.substr(pos, 2) == "\\n") { + cur += '\n'; + pos += 2; + } + // Backslash escape + else if (inp.length() >= pos + 2 && inp[pos] == '\\') { + cur += inp[pos + 1]; + pos += 2; + } + // Normal character + else { + cur += inp[pos]; + pos += 1; + } + } + else { + // Handle atoms ( '//', '#', brackets ) + for (int i = 0; i < numAtoms; i++) { + int split = cur.length() - atoms[i].length(); + if (split >= 0 && cur.substr(split) == atoms[i]) { + if (split > 0) { + out.push_back(token(cur.substr(0, split), metadata)); + } + metadata.ch += split; + out.push_back(token(cur.substr(split), metadata)); + metadata.ch = pos - lastNewline; + cur = ""; + curtype = SPACE; + } + } + // Special case the minus sign + if (cur.length() > 1 && (cur.substr(cur.length() - 1) == "-" + || cur.substr(cur.length() - 1) == "!")) { + out.push_back(token(cur.substr(0, cur.length() - 1), metadata)); + out.push_back(token(cur.substr(cur.length() - 1), metadata)); + cur = ""; + } + // Boundary between different char types + if (headtype != curtype) { + if (curtype != SPACE && cur != "") { + out.push_back(token(cur, metadata)); + } + metadata.ch = pos - lastNewline; + cur = ""; + } + cur += inp[pos]; + curtype = headtype; + pos += 1; + } + if (inp[pos] == '\n') { + lastNewline = pos; + metadata.ch = 0; + metadata.ln += 1; + } + } + return out; +} + + diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.h new file mode 100644 index 000000000..04a42f3c6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/tokenize.h @@ -0,0 +1,16 @@ +#ifndef ETHSERP_TOKENIZE +#define ETHSERP_TOKENIZE + +#include +#include +#include +#include +#include "util.h" + +int chartype(char c); + +std::vector tokenize(std::string inp, + Metadata meta=Metadata(), + bool lispMode=false); + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.cpp b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.cpp new file mode 100644 index 000000000..56f642fc8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.cpp @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include "util.h" +#include "bignum.h" +#include +#include + +//Token or value node constructor +Node token(std::string val, Metadata met) { + Node o; + o.type = 0; + o.val = val; + o.metadata = met; + return o; +} + +//AST node constructor +Node astnode(std::string val, std::vector args, Metadata met) { + Node o; + o.type = 1; + o.val = val; + o.args = args; + o.metadata = met; + return o; +} + +//AST node constructors for a specific number of children +Node astnode(std::string val, Metadata met) { + std::vector args; + return astnode(val, args, met); +} + +Node astnode(std::string val, Node a, Metadata met) { + std::vector args; + args.push_back(a); + return astnode(val, args, met); +} + +Node astnode(std::string val, Node a, Node b, Metadata met) { + std::vector args; + args.push_back(a); + args.push_back(b); + return astnode(val, args, met); +} + +Node astnode(std::string val, Node a, Node b, Node c, Metadata met) { + std::vector args; + args.push_back(a); + args.push_back(b); + args.push_back(c); + return astnode(val, args, met); +} + +Node astnode(std::string val, Node a, Node b, Node c, Node d, Metadata met) { + std::vector args; + args.push_back(a); + args.push_back(b); + args.push_back(c); + args.push_back(d); + return astnode(val, args, met); +} + + +// Print token list +std::string printTokens(std::vector tokens) { + std::string s = ""; + for (unsigned i = 0; i < tokens.size(); i++) { + s += tokens[i].val + " "; + } + return s; +} + +// Prints a lisp AST on one line +std::string printSimple(Node ast) { + if (ast.type == TOKEN) return ast.val; + std::string o = "(" + ast.val; + std::vector subs; + for (unsigned i = 0; i < ast.args.size(); i++) { + o += " " + printSimple(ast.args[i]); + } + return o + ")"; +} + +// Number of tokens in a tree +int treeSize(Node prog) { + if (prog.type == TOKEN) return 1; + int o = 0; + for (unsigned i = 0; i < prog.args.size(); i++) o += treeSize(prog.args[i]); + return o; +} + +// Pretty-prints a lisp AST +std::string printAST(Node ast, bool printMetadata) { + if (ast.type == TOKEN) return ast.val; + std::string o = "("; + if (printMetadata) { + o += ast.metadata.file + " "; + o += unsignedToDecimal(ast.metadata.ln) + " "; + o += unsignedToDecimal(ast.metadata.ch) + ": "; + } + o += ast.val; + std::vector subs; + for (unsigned i = 0; i < ast.args.size(); i++) { + subs.push_back(printAST(ast.args[i], printMetadata)); + } + unsigned k = 0; + std::string out = " "; + // As many arguments as possible go on the same line as the function, + // except when seq is used + while (k < subs.size() && o != "(seq") { + if (subs[k].find("\n") != std::string::npos || (out + subs[k]).length() >= 80) break; + out += subs[k] + " "; + k += 1; + } + // All remaining arguments go on their own lines + if (k < subs.size()) { + o += out + "\n"; + std::vector subsSliceK; + for (unsigned i = k; i < subs.size(); i++) subsSliceK.push_back(subs[i]); + o += indentLines(joinLines(subsSliceK)); + o += "\n)"; + } + else { + o += out.substr(0, out.size() - 1) + ")"; + } + return o; +} + +// Splits text by line +std::vector splitLines(std::string s) { + unsigned pos = 0; + int lastNewline = 0; + std::vector o; + while (pos < s.length()) { + if (s[pos] == '\n') { + o.push_back(s.substr(lastNewline, pos - lastNewline)); + lastNewline = pos + 1; + } + pos = pos + 1; + } + o.push_back(s.substr(lastNewline)); + return o; +} + +// Inverse of splitLines +std::string joinLines(std::vector lines) { + std::string o = "\n"; + for (unsigned i = 0; i < lines.size(); i++) { + o += lines[i] + "\n"; + } + return o.substr(1, o.length() - 2); +} + +// Indent all lines by 4 spaces +std::string indentLines(std::string inp) { + std::vector lines = splitLines(inp); + for (unsigned i = 0; i < lines.size(); i++) lines[i] = " "+lines[i]; + return joinLines(lines); +} + +// Binary to hexadecimal +std::string binToNumeric(std::string inp) { + std::string o = "0"; + for (unsigned i = 0; i < inp.length(); i++) { + o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); + } + return o; +} + +// Converts string to simple numeric format +std::string strToNumeric(std::string inp) { + std::string o = "0"; + if (inp == "") { + o = ""; + } + else if (inp.substr(0,2) == "0x") { + for (unsigned i = 2; i < inp.length(); i++) { + int dig = std::string("0123456789abcdef0123456789ABCDEF").find(inp[i]) % 16; + if (dig < 0) return ""; + o = decimalAdd(decimalMul(o,"16"), unsignedToDecimal(dig)); + } + } + else { + bool isPureNum = true; + for (unsigned i = 0; i < inp.length(); i++) { + isPureNum = isPureNum && inp[i] >= '0' && inp[i] <= '9'; + } + o = isPureNum ? inp : ""; + } + return o; +} + +// Does the node contain a number (eg. 124, 0xf012c, "george") +bool isNumberLike(Node node) { + if (node.type == ASTNODE) return false; + return strToNumeric(node.val) != ""; +} + +//Normalizes number representations +Node nodeToNumeric(Node node) { + std::string o = strToNumeric(node.val); + return token(o == "" ? node.val : o, node.metadata); +} + +Node tryNumberize(Node node) { + if (node.type == TOKEN && isNumberLike(node)) return nodeToNumeric(node); + return node; +} + +//Converts a value to an array of byte number nodes +std::vector toByteArr(std::string val, Metadata metadata, int minLen) { + std::vector o; + int L = 0; + while (val != "0" || L < minLen) { + o.push_back(token(decimalMod(val, "256"), metadata)); + val = decimalDiv(val, "256"); + L++; + } + std::vector o2; + for (int i = o.size() - 1; i >= 0; i--) o2.push_back(o[i]); + return o2; +} + +int counter = 0; + +//Makes a unique token +std::string mkUniqueToken() { + counter++; + return unsignedToDecimal(counter); +} + +//Does a file exist? http://stackoverflow.com/questions/12774207 +bool exists(std::string fileName) { + std::ifstream infile(fileName.c_str()); + return infile.good(); +} + +//Reads a file: http://stackoverflow.com/questions/2602013 +std::string get_file_contents(std::string filename) +{ + std::ifstream in(filename.c_str(), std::ios::in | std::ios::binary); + if (in) + { + std::string contents; + in.seekg(0, std::ios::end); + contents.resize(in.tellg()); + in.seekg(0, std::ios::beg); + in.read(&contents[0], contents.size()); + in.close(); + return(contents); + } + throw(errno); +} + +//Report error +void err(std::string errtext, Metadata met) { + std::string err = "Error (file \"" + met.file + "\", line " + + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + + "): " + errtext; + std::cerr << err << "\n"; + throw(err); +} + +//Bin to hex +std::string binToHex(std::string inp) { + std::string o = ""; + for (unsigned i = 0; i < inp.length(); i++) { + unsigned char v = inp[i]; + o += std::string("0123456789abcdef").substr(v/16, 1) + + std::string("0123456789abcdef").substr(v%16, 1); + } + return o; +} + +//Hex to bin +std::string hexToBin(std::string inp) { + std::string o = ""; + for (unsigned i = 0; i+1 < inp.length(); i+=2) { + char v = (char)(std::string("0123456789abcdef").find(inp[i]) * 16 + + std::string("0123456789abcdef").find(inp[i+1])); + o += v; + } + return o; +} + +//Lower to upper +std::string upperCase(std::string inp) { + std::string o = ""; + for (unsigned i = 0; i < inp.length(); i++) { + if (inp[i] >= 97 && inp[i] <= 122) o += inp[i] - 32; + else o += inp[i]; + } + return o; +} + +//Three-int vector +std::vector triple(int a, int b, int c) { + std::vector v; + v.push_back(a); + v.push_back(b); + v.push_back(c); + return v; +} diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.h b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.h new file mode 100644 index 000000000..f7d6744f9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/util.h @@ -0,0 +1,127 @@ +#ifndef ETHSERP_UTIL +#define ETHSERP_UTIL + +#include +#include +#include +#include +#include +#include + +const int TOKEN = 0, + ASTNODE = 1, + SPACE = 2, + BRACK = 3, + SQUOTE = 4, + DQUOTE = 5, + SYMB = 6, + ALPHANUM = 7, + LPAREN = 8, + RPAREN = 9, + COMMA = 10, + COLON = 11, + UNARY_OP = 12, + BINARY_OP = 13, + COMPOUND = 14, + TOKEN_SPLITTER = 15; + +// Stores metadata about each token +class Metadata { + public: + Metadata(std::string File="main", int Ln=-1, int Ch=-1) { + file = File; + ln = Ln; + ch = Ch; + fixed = false; + } + std::string file; + int ln; + int ch; + bool fixed; +}; + +std::string mkUniqueToken(); + +// type can be TOKEN or ASTNODE +struct Node { + int type; + std::string val; + std::vector args; + Metadata metadata; +}; +Node token(std::string val, Metadata met=Metadata()); +Node astnode(std::string val, std::vector args, Metadata met=Metadata()); +Node astnode(std::string val, Metadata met=Metadata()); +Node astnode(std::string val, Node a, Metadata met=Metadata()); +Node astnode(std::string val, Node a, Node b, Metadata met=Metadata()); +Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata()); +Node astnode(std::string val, Node a, Node b, + Node c, Node d, Metadata met=Metadata()); + +// Number of tokens in a tree +int treeSize(Node prog); + +// Print token list +std::string printTokens(std::vector tokens); + +// Prints a lisp AST on one line +std::string printSimple(Node ast); + +// Pretty-prints a lisp AST +std::string printAST(Node ast, bool printMetadata=false); + +// Splits text by line +std::vector splitLines(std::string s); + +// Inverse of splitLines +std::string joinLines(std::vector lines); + +// Indent all lines by 4 spaces +std::string indentLines(std::string inp); + +// Converts binary to simple numeric format +std::string binToNumeric(std::string inp); + +// Converts string to simple numeric format +std::string strToNumeric(std::string inp); + +// Does the node contain a number (eg. 124, 0xf012c, "george") +bool isNumberLike(Node node); + +//Normalizes number representations +Node nodeToNumeric(Node node); + +//If a node is numeric, normalize its representation +Node tryNumberize(Node node); + +//Converts a value to an array of byte number nodes +std::vector toByteArr(std::string val, Metadata metadata, int minLen=1); + +//Reads a file +std::string get_file_contents(std::string filename); + +//Does a file exist? +bool exists(std::string fileName); + +//Report error +void err(std::string errtext, Metadata met); + +//Bin to hex +std::string binToHex(std::string inp); + +//Hex to bin +std::string hexToBin(std::string inp); + +//Lower to upper +std::string upperCase(std::string inp); + +//Three-int vector +std::vector triple(int a, int b, int c); + +#define asn astnode +#define tkn token +#define msi std::map +#define msn std::map +#define mss std::map + +#endif diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/tests/main.go b/Godeps/_workspace/src/github.com/ethereum/serpent-go/tests/main.go new file mode 100644 index 000000000..2f2d17784 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/tests/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + + "github.com/ethereum/serpent-go" +) + +func main() { + out, _ := serpent.Compile(` +// Namecoin +if !contract.storage[msg.data[0]]: # Is the key not yet taken? + # Then take it! + contract.storage[msg.data[0]] = msg.data[1] + return(1) +else: + return(0) // Otherwise do nothing + `) + + fmt.Printf("%x\n", out) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/LICENSE b/Godeps/_workspace/src/github.com/fjl/goupnp/LICENSE new file mode 100644 index 000000000..252e3d639 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013, John Beisley +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/README.md b/Godeps/_workspace/src/github.com/fjl/goupnp/README.md new file mode 100644 index 000000000..d464c8288 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/README.md @@ -0,0 +1,14 @@ +goupnp is a UPnP client library for Go + +Installation +------------ + +Run `go get -u github.com/fjl/goupnp`. + +Regenerating dcps generated source code: +---------------------------------------- + +1. Install gotasks: `go get -u github.com/jingweno/gotask` +2. Change to the gotasks directory: `cd gotasks` +3. Download UPnP specification data (if not done already): `wget http://upnp.org/resources/upnpresources.zip` +4. Regenerate source code: `gotask specgen -s upnpresources.zip -o ../dcps` diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_httpu_serving/example_httpu_serving.go b/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_httpu_serving/example_httpu_serving.go new file mode 100644 index 000000000..d56d39572 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_httpu_serving/example_httpu_serving.go @@ -0,0 +1,20 @@ +package main + +import ( + "log" + "net/http" + + "github.com/fjl/goupnp/httpu" +) + +func main() { + srv := httpu.Server{ + Addr: "239.255.255.250:1900", + Multicast: true, + Handler: httpu.HandlerFunc(func(r *http.Request) { + log.Printf("Got %s %s message from %v: %v", r.Method, r.URL.Path, r.RemoteAddr, r.Header) + }), + } + err := srv.ListenAndServe() + log.Printf("Serving failed with error: %v", err) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_internetgateway1/example_internetgateway1.go b/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_internetgateway1/example_internetgateway1.go new file mode 100644 index 000000000..fda612500 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/cmd/example_internetgateway1/example_internetgateway1.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "log" + + "github.com/fjl/goupnp/dcps/internetgateway1" +) + +func main() { + clients, errors, err := internetgateway1.NewWANPPPConnection1Clients() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Got %d errors finding servers and %d successfully discovered.\n", + len(errors), len(clients)) + for i, e := range errors { + fmt.Printf("Error finding server #%d: %v\n", i+1, e) + } + + for _, c := range clients { + dev := &c.ServiceClient.RootDevice.Device + srv := c.ServiceClient.Service + fmt.Println(dev.FriendlyName, " :: ", srv.String()) + scpd, err := srv.RequestSCDP() + if err != nil { + fmt.Printf(" Error requesting service SCPD: %v\n", err) + } else { + fmt.Println(" Available actions:") + for _, action := range scpd.Actions { + fmt.Printf(" * %s\n", action.Name) + for _, arg := range action.Arguments { + var varDesc string + if stateVar := scpd.GetStateVariable(arg.RelatedStateVariable); stateVar != nil { + varDesc = fmt.Sprintf(" (%s)", stateVar.DataType.Name) + } + fmt.Printf(" * [%s] %s%s\n", arg.Direction, arg.Name, varDesc) + } + } + } + + if scpd == nil || scpd.GetAction("GetExternalIPAddress") != nil { + ip, err := c.GetExternalIPAddress() + fmt.Println("GetExternalIPAddress: ", ip, err) + } + + if scpd == nil || scpd.GetAction("GetStatusInfo") != nil { + status, lastErr, uptime, err := c.GetStatusInfo() + fmt.Println("GetStatusInfo: ", status, lastErr, uptime, err) + } + + if scpd == nil || scpd.GetAction("GetIdleDisconnectTime") != nil { + idleTime, err := c.GetIdleDisconnectTime() + fmt.Println("GetIdleDisconnectTime: ", idleTime, err) + } + + if scpd == nil || scpd.GetAction("AddPortMapping") != nil { + err := c.AddPortMapping("", 5000, "TCP", 5001, "192.168.1.2", true, "Test port mapping", 0) + fmt.Println("AddPortMapping: ", err) + } + if scpd == nil || scpd.GetAction("DeletePortMapping") != nil { + err := c.DeletePortMapping("", 5000, "TCP") + fmt.Println("DeletePortMapping: ", err) + } + } +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway1/internetgateway1.go b/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway1/internetgateway1.go new file mode 100644 index 000000000..49659722c --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway1/internetgateway1.go @@ -0,0 +1,3957 @@ +// Client for UPnP Device Control Protocol Internet Gateway Device v1. +// +// This DCP is documented in detail at: http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf +// +// Typically, use one of the New* functions to discover services on the local +// network. +package internetgateway1 + +// Generated file - do not edit by hand. See README.md + +import ( + "time" + + "github.com/fjl/goupnp" + "github.com/fjl/goupnp/soap" +) + +// Hack to avoid Go complaining if time isn't used. +var _ time.Time + +// Device URNs: +const ( + URN_LANDevice_1 = "urn:schemas-upnp-org:device:LANDevice:1" + URN_WANConnectionDevice_1 = "urn:schemas-upnp-org:device:WANConnectionDevice:1" + URN_WANDevice_1 = "urn:schemas-upnp-org:device:WANDevice:1" +) + +// Service URNs: +const ( + URN_LANHostConfigManagement_1 = "urn:schemas-upnp-org:service:LANHostConfigManagement:1" + URN_Layer3Forwarding_1 = "urn:schemas-upnp-org:service:Layer3Forwarding:1" + URN_WANCableLinkConfig_1 = "urn:schemas-upnp-org:service:WANCableLinkConfig:1" + URN_WANCommonInterfaceConfig_1 = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" + URN_WANDSLLinkConfig_1 = "urn:schemas-upnp-org:service:WANDSLLinkConfig:1" + URN_WANEthernetLinkConfig_1 = "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" + URN_WANIPConnection_1 = "urn:schemas-upnp-org:service:WANIPConnection:1" + URN_WANPOTSLinkConfig_1 = "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1" + URN_WANPPPConnection_1 = "urn:schemas-upnp-org:service:WANPPPConnection:1" +) + +// LANHostConfigManagement1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:LANHostConfigManagement:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type LANHostConfigManagement1 struct { + goupnp.ServiceClient +} + +// NewLANHostConfigManagement1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLANHostConfigManagement1Clients() (clients []*LANHostConfigManagement1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_LANHostConfigManagement_1); err != nil { + return + } + clients = make([]*LANHostConfigManagement1, len(genericClients)) + for i := range genericClients { + clients[i] = &LANHostConfigManagement1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewDHCPServerConfigurable: +// +// +func (client *LANHostConfigManagement1) SetDHCPServerConfigurable(NewDHCPServerConfigurable bool) (err error) { + // Request structure. + request := &struct { + NewDHCPServerConfigurable string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPServerConfigurable, err = soap.MarshalBoolean(NewDHCPServerConfigurable); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDHCPServerConfigurable: +func (client *LANHostConfigManagement1) GetDHCPServerConfigurable() (NewDHCPServerConfigurable bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPServerConfigurable string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPServerConfigurable, err = soap.UnmarshalBoolean(response.NewDHCPServerConfigurable); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDHCPRelay: +// +// +func (client *LANHostConfigManagement1) SetDHCPRelay(NewDHCPRelay bool) (err error) { + // Request structure. + request := &struct { + NewDHCPRelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPRelay, err = soap.MarshalBoolean(NewDHCPRelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDHCPRelay: +func (client *LANHostConfigManagement1) GetDHCPRelay() (NewDHCPRelay bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPRelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPRelay, err = soap.UnmarshalBoolean(response.NewDHCPRelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewSubnetMask: +// +// +func (client *LANHostConfigManagement1) SetSubnetMask(NewSubnetMask string) (err error) { + // Request structure. + request := &struct { + NewSubnetMask string + }{} + // BEGIN Marshal arguments into request. + + if request.NewSubnetMask, err = soap.MarshalString(NewSubnetMask); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewSubnetMask: +func (client *LANHostConfigManagement1) GetSubnetMask() (NewSubnetMask string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewSubnetMask string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewSubnetMask, err = soap.UnmarshalString(response.NewSubnetMask); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIPRouters: +// +// +func (client *LANHostConfigManagement1) SetIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIPRouters: +// +// +func (client *LANHostConfigManagement1) DeleteIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIPRouters: +func (client *LANHostConfigManagement1) GetIPRoutersList() (NewIPRouters string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIPRouters string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetIPRoutersList", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIPRouters, err = soap.UnmarshalString(response.NewIPRouters); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDomainName: +// +// +func (client *LANHostConfigManagement1) SetDomainName(NewDomainName string) (err error) { + // Request structure. + request := &struct { + NewDomainName string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDomainName, err = soap.MarshalString(NewDomainName); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDomainName: +func (client *LANHostConfigManagement1) GetDomainName() (NewDomainName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDomainName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDomainName, err = soap.UnmarshalString(response.NewDomainName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewMinAddress: +// +// * NewMaxAddress: +// +// +func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, NewMaxAddress string) (err error) { + // Request structure. + request := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewMinAddress, err = soap.MarshalString(NewMinAddress); err != nil { + return + } + if request.NewMaxAddress, err = soap.MarshalString(NewMaxAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewMinAddress: +// +// * NewMaxAddress: +func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string, NewMaxAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMinAddress, err = soap.UnmarshalString(response.NewMinAddress); err != nil { + return + } + if NewMaxAddress, err = soap.UnmarshalString(response.NewMaxAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewReservedAddresses: +// +// +func (client *LANHostConfigManagement1) SetReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewReservedAddresses: +// +// +func (client *LANHostConfigManagement1) DeleteReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewReservedAddresses: +func (client *LANHostConfigManagement1) GetReservedAddresses() (NewReservedAddresses string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedAddresses string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetReservedAddresses", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedAddresses, err = soap.UnmarshalString(response.NewReservedAddresses); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDNSServers: +// +// +func (client *LANHostConfigManagement1) SetDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDNSServers: +// +// +func (client *LANHostConfigManagement1) DeleteDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDNSServers: +func (client *LANHostConfigManagement1) GetDNSServers() (NewDNSServers string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDNSServers string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDNSServers", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDNSServers, err = soap.UnmarshalString(response.NewDNSServers); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Layer3Forwarding1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:Layer3Forwarding:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type Layer3Forwarding1 struct { + goupnp.ServiceClient +} + +// NewLayer3Forwarding1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLayer3Forwarding1Clients() (clients []*Layer3Forwarding1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_Layer3Forwarding_1); err != nil { + return + } + clients = make([]*Layer3Forwarding1, len(genericClients)) + for i := range genericClients { + clients[i] = &Layer3Forwarding1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewDefaultConnectionService: +// +// +func (client *Layer3Forwarding1) SetDefaultConnectionService(NewDefaultConnectionService string) (err error) { + // Request structure. + request := &struct { + NewDefaultConnectionService string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDefaultConnectionService, err = soap.MarshalString(NewDefaultConnectionService); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "SetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDefaultConnectionService: +func (client *Layer3Forwarding1) GetDefaultConnectionService() (NewDefaultConnectionService string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDefaultConnectionService string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "GetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDefaultConnectionService, err = soap.UnmarshalString(response.NewDefaultConnectionService); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCableLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCableLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCableLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCableLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCableLinkConfig1Clients() (clients []*WANCableLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCableLinkConfig_1); err != nil { + return + } + clients = make([]*WANCableLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCableLinkConfig1{genericClients[i]} + } + return +} + +// +// +// Return values: +// +// * NewCableLinkConfigState: allowed values: notReady, dsSyncComplete, usParamAcquired, rangingComplete, ipComplete, todEstablished, paramTransferComplete, registrationComplete, operational, accessDenied +// +// * NewLinkType: allowed values: Ethernet +func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigState string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewCableLinkConfigState string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetCableLinkConfigInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewCableLinkConfigState, err = soap.UnmarshalString(response.NewCableLinkConfigState); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDownstreamFrequency: +func (client *WANCableLinkConfig1) GetDownstreamFrequency() (NewDownstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamFrequency, err = soap.UnmarshalUi4(response.NewDownstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDownstreamModulation: allowed values: 64QAM, 256QAM +func (client *WANCableLinkConfig1) GetDownstreamModulation() (NewDownstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamModulation, err = soap.UnmarshalString(response.NewDownstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamFrequency: +func (client *WANCableLinkConfig1) GetUpstreamFrequency() (NewUpstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamFrequency, err = soap.UnmarshalUi4(response.NewUpstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamModulation: allowed values: QPSK, 16QAM +func (client *WANCableLinkConfig1) GetUpstreamModulation() (NewUpstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamModulation, err = soap.UnmarshalString(response.NewUpstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamChannelID: +func (client *WANCableLinkConfig1) GetUpstreamChannelID() (NewUpstreamChannelID uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamChannelID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamChannelID", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamChannelID, err = soap.UnmarshalUi4(response.NewUpstreamChannelID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamPowerLevel: +func (client *WANCableLinkConfig1) GetUpstreamPowerLevel() (NewUpstreamPowerLevel uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamPowerLevel string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamPowerLevel", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamPowerLevel, err = soap.UnmarshalUi4(response.NewUpstreamPowerLevel); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewBPIEncryptionEnabled: +func (client *WANCableLinkConfig1) GetBPIEncryptionEnabled() (NewBPIEncryptionEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewBPIEncryptionEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetBPIEncryptionEnabled", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewBPIEncryptionEnabled, err = soap.UnmarshalBoolean(response.NewBPIEncryptionEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConfigFile: +func (client *WANCableLinkConfig1) GetConfigFile() (NewConfigFile string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConfigFile string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetConfigFile", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConfigFile, err = soap.UnmarshalString(response.NewConfigFile); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTFTPServer: +func (client *WANCableLinkConfig1) GetTFTPServer() (NewTFTPServer string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTFTPServer string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetTFTPServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTFTPServer, err = soap.UnmarshalString(response.NewTFTPServer); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCommonInterfaceConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCommonInterfaceConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCommonInterfaceConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCommonInterfaceConfig1Clients() (clients []*WANCommonInterfaceConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCommonInterfaceConfig_1); err != nil { + return + } + clients = make([]*WANCommonInterfaceConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCommonInterfaceConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewEnabledForInternet: +// +// +func (client *WANCommonInterfaceConfig1) SetEnabledForInternet(NewEnabledForInternet bool) (err error) { + // Request structure. + request := &struct { + NewEnabledForInternet string + }{} + // BEGIN Marshal arguments into request. + + if request.NewEnabledForInternet, err = soap.MarshalBoolean(NewEnabledForInternet); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "SetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewEnabledForInternet: +func (client *WANCommonInterfaceConfig1) GetEnabledForInternet() (NewEnabledForInternet bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEnabledForInternet string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEnabledForInternet, err = soap.UnmarshalBoolean(response.NewEnabledForInternet); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWANAccessType: allowed values: DSL, POTS, Cable, Ethernet +// +// * NewLayer1UpstreamMaxBitRate: +// +// * NewLayer1DownstreamMaxBitRate: +// +// * NewPhysicalLinkStatus: allowed values: Up, Down +func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccessType string, NewLayer1UpstreamMaxBitRate uint32, NewLayer1DownstreamMaxBitRate uint32, NewPhysicalLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessType string + + NewLayer1UpstreamMaxBitRate string + + NewLayer1DownstreamMaxBitRate string + + NewPhysicalLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetCommonLinkProperties", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessType, err = soap.UnmarshalString(response.NewWANAccessType); err != nil { + return + } + if NewLayer1UpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1UpstreamMaxBitRate); err != nil { + return + } + if NewLayer1DownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1DownstreamMaxBitRate); err != nil { + return + } + if NewPhysicalLinkStatus, err = soap.UnmarshalString(response.NewPhysicalLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWANAccessProvider: +func (client *WANCommonInterfaceConfig1) GetWANAccessProvider() (NewWANAccessProvider string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessProvider string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetWANAccessProvider", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessProvider, err = soap.UnmarshalString(response.NewWANAccessProvider); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewMaximumActiveConnections: allowed value range: minimum=1, step=1 +func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaximumActiveConnections uint16, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMaximumActiveConnections string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetMaximumActiveConnections", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMaximumActiveConnections, err = soap.UnmarshalUi2(response.NewMaximumActiveConnections); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalBytesSent: +func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalBytesReceived: +func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalPacketsSent: +func (client *WANCommonInterfaceConfig1) GetTotalPacketsSent() (NewTotalPacketsSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsSent, err = soap.UnmarshalUi4(response.NewTotalPacketsSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalPacketsReceived: +func (client *WANCommonInterfaceConfig1) GetTotalPacketsReceived() (NewTotalPacketsReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsReceived, err = soap.UnmarshalUi4(response.NewTotalPacketsReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewActiveConnectionIndex: +// +// Return values: +// +// * NewActiveConnDeviceContainer: +// +// * NewActiveConnectionServiceID: +func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnectionIndex uint16) (NewActiveConnDeviceContainer string, NewActiveConnectionServiceID string, err error) { + // Request structure. + request := &struct { + NewActiveConnectionIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewActiveConnectionIndex, err = soap.MarshalUi2(NewActiveConnectionIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewActiveConnDeviceContainer string + + NewActiveConnectionServiceID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetActiveConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewActiveConnDeviceContainer, err = soap.UnmarshalString(response.NewActiveConnDeviceContainer); err != nil { + return + } + if NewActiveConnectionServiceID, err = soap.UnmarshalString(response.NewActiveConnectionServiceID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANDSLLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANDSLLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANDSLLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANDSLLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANDSLLinkConfig1Clients() (clients []*WANDSLLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANDSLLinkConfig_1); err != nil { + return + } + clients = make([]*WANDSLLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANDSLLinkConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewLinkType: +// +// +func (client *WANDSLLinkConfig1) SetDSLLinkType(NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDSLLinkType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewLinkType: +// +// * NewLinkStatus: allowed values: Up, Down +func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewLinkType string + + NewLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDSLLinkInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + if NewLinkStatus, err = soap.UnmarshalString(response.NewLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoConfig: +func (client *WANDSLLinkConfig1) GetAutoConfig() (NewAutoConfig bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoConfig string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetAutoConfig", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoConfig, err = soap.UnmarshalBoolean(response.NewAutoConfig); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewModulationType: +func (client *WANDSLLinkConfig1) GetModulationType() (NewModulationType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewModulationType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetModulationType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewModulationType, err = soap.UnmarshalString(response.NewModulationType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDestinationAddress: +// +// +func (client *WANDSLLinkConfig1) SetDestinationAddress(NewDestinationAddress string) (err error) { + // Request structure. + request := &struct { + NewDestinationAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDestinationAddress, err = soap.MarshalString(NewDestinationAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDestinationAddress: +func (client *WANDSLLinkConfig1) GetDestinationAddress() (NewDestinationAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDestinationAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDestinationAddress, err = soap.UnmarshalString(response.NewDestinationAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewATMEncapsulation: +// +// +func (client *WANDSLLinkConfig1) SetATMEncapsulation(NewATMEncapsulation string) (err error) { + // Request structure. + request := &struct { + NewATMEncapsulation string + }{} + // BEGIN Marshal arguments into request. + + if request.NewATMEncapsulation, err = soap.MarshalString(NewATMEncapsulation); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewATMEncapsulation: +func (client *WANDSLLinkConfig1) GetATMEncapsulation() (NewATMEncapsulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewATMEncapsulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewATMEncapsulation, err = soap.UnmarshalString(response.NewATMEncapsulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewFCSPreserved: +// +// +func (client *WANDSLLinkConfig1) SetFCSPreserved(NewFCSPreserved bool) (err error) { + // Request structure. + request := &struct { + NewFCSPreserved string + }{} + // BEGIN Marshal arguments into request. + + if request.NewFCSPreserved, err = soap.MarshalBoolean(NewFCSPreserved); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewFCSPreserved: +func (client *WANDSLLinkConfig1) GetFCSPreserved() (NewFCSPreserved bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFCSPreserved string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFCSPreserved, err = soap.UnmarshalBoolean(response.NewFCSPreserved); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANEthernetLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANEthernetLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANEthernetLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANEthernetLinkConfig1Clients() (clients []*WANEthernetLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANEthernetLinkConfig_1); err != nil { + return + } + clients = make([]*WANEthernetLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANEthernetLinkConfig1{genericClients[i]} + } + return +} + +// +// +// Return values: +// +// * NewEthernetLinkStatus: allowed values: Up, Down +func (client *WANEthernetLinkConfig1) GetEthernetLinkStatus() (NewEthernetLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEthernetLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANEthernetLinkConfig_1, "GetEthernetLinkStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEthernetLinkStatus, err = soap.UnmarshalString(response.NewEthernetLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection1Clients() (clients []*WANIPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_1); err != nil { + return + } + clients = make([]*WANIPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewConnectionType: +// +// +func (client *WANIPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionType: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, IP_Bridged +func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewAutoDisconnectTime: +// +// +func (client *WANIPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIdleDisconnectTime: +// +// +func (client *WANIPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewWarnDisconnectDelay: +// +// +func (client *WANIPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +// +// * NewUptime: +func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoDisconnectTime: +func (client *WANIPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIdleDisconnectTime: +func (client *WANIPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWarnDisconnectDelay: +func (client *WANIPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewRSIPAvailable: +// +// * NewNATEnabled: +func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewPortMappingIndex: +// +// Return values: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// Return values: +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +// +// +func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// +func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewExternalIPAddress: +func (client *WANIPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPOTSLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPOTSLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANPOTSLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPOTSLinkConfig1Clients() (clients []*WANPOTSLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPOTSLinkConfig_1); err != nil { + return + } + clients = make([]*WANPOTSLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPOTSLinkConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewISPPhoneNumber: +// +// * NewISPInfo: +// +// * NewLinkType: allowed values: PPP_Dialup +// +// +func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInfo string, NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewISPPhoneNumber, err = soap.MarshalString(NewISPPhoneNumber); err != nil { + return + } + if request.NewISPInfo, err = soap.MarshalString(NewISPInfo); err != nil { + return + } + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewNumberOfRetries: +// +// * NewDelayBetweenRetries: +// +// +func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, NewDelayBetweenRetries uint32) (err error) { + // Request structure. + request := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + // BEGIN Marshal arguments into request. + + if request.NewNumberOfRetries, err = soap.MarshalUi4(NewNumberOfRetries); err != nil { + return + } + if request.NewDelayBetweenRetries, err = soap.MarshalUi4(NewDelayBetweenRetries); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewISPPhoneNumber: +// +// * NewISPInfo: +// +// * NewLinkType: allowed values: PPP_Dialup +func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISPInfo string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewISPPhoneNumber, err = soap.UnmarshalString(response.NewISPPhoneNumber); err != nil { + return + } + if NewISPInfo, err = soap.UnmarshalString(response.NewISPInfo); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewNumberOfRetries: +// +// * NewDelayBetweenRetries: +func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32, NewDelayBetweenRetries uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewNumberOfRetries, err = soap.UnmarshalUi4(response.NewNumberOfRetries); err != nil { + return + } + if NewDelayBetweenRetries, err = soap.UnmarshalUi4(response.NewDelayBetweenRetries); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewFclass: +func (client *WANPOTSLinkConfig1) GetFclass() (NewFclass string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFclass string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetFclass", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFclass, err = soap.UnmarshalString(response.NewFclass); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataModulationSupported: +func (client *WANPOTSLinkConfig1) GetDataModulationSupported() (NewDataModulationSupported string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataModulationSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataModulationSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataModulationSupported, err = soap.UnmarshalString(response.NewDataModulationSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataProtocol: +func (client *WANPOTSLinkConfig1) GetDataProtocol() (NewDataProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataProtocol, err = soap.UnmarshalString(response.NewDataProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataCompression: +func (client *WANPOTSLinkConfig1) GetDataCompression() (NewDataCompression string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataCompression string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataCompression", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataCompression, err = soap.UnmarshalString(response.NewDataCompression); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPlusVTRCommandSupported: +func (client *WANPOTSLinkConfig1) GetPlusVTRCommandSupported() (NewPlusVTRCommandSupported bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPlusVTRCommandSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetPlusVTRCommandSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPlusVTRCommandSupported, err = soap.UnmarshalBoolean(response.NewPlusVTRCommandSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPPPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPPPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPPPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANPPPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPPPConnection1Clients() (clients []*WANPPPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPPPConnection_1); err != nil { + return + } + clients = make([]*WANPPPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPPPConnection1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewConnectionType: +// +// +func (client *WANPPPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionType: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, DHCP_Spoofed, PPPoE_Bridged, PPTP_Relay, L2TP_Relay, PPPoE_Relay +func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewUserName: +// +// * NewPassword: +// +// +func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPassword string) (err error) { + // Request structure. + request := &struct { + NewUserName string + + NewPassword string + }{} + // BEGIN Marshal arguments into request. + + if request.NewUserName, err = soap.MarshalString(NewUserName); err != nil { + return + } + if request.NewPassword, err = soap.MarshalString(NewPassword); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ConfigureConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewAutoDisconnectTime: +// +// +func (client *WANPPPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIdleDisconnectTime: +// +// +func (client *WANPPPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewWarnDisconnectDelay: +// +// +func (client *WANPPPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +// +// * NewUptime: +func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamMaxBitRate: +// +// * NewDownstreamMaxBitRate: +func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRate uint32, NewDownstreamMaxBitRate uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamMaxBitRate string + + NewDownstreamMaxBitRate string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetLinkLayerMaxBitRates", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewUpstreamMaxBitRate); err != nil { + return + } + if NewDownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewDownstreamMaxBitRate); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPEncryptionProtocol: +func (client *WANPPPConnection1) GetPPPEncryptionProtocol() (NewPPPEncryptionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPEncryptionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPEncryptionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPEncryptionProtocol, err = soap.UnmarshalString(response.NewPPPEncryptionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPCompressionProtocol: +func (client *WANPPPConnection1) GetPPPCompressionProtocol() (NewPPPCompressionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPCompressionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPCompressionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPCompressionProtocol, err = soap.UnmarshalString(response.NewPPPCompressionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPAuthenticationProtocol: +func (client *WANPPPConnection1) GetPPPAuthenticationProtocol() (NewPPPAuthenticationProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPAuthenticationProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPAuthenticationProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPAuthenticationProtocol, err = soap.UnmarshalString(response.NewPPPAuthenticationProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUserName: +func (client *WANPPPConnection1) GetUserName() (NewUserName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUserName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetUserName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUserName, err = soap.UnmarshalString(response.NewUserName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPassword: +func (client *WANPPPConnection1) GetPassword() (NewPassword string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPassword string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPassword", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPassword, err = soap.UnmarshalString(response.NewPassword); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoDisconnectTime: +func (client *WANPPPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIdleDisconnectTime: +func (client *WANPPPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWarnDisconnectDelay: +func (client *WANPPPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewRSIPAvailable: +// +// * NewNATEnabled: +func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewPortMappingIndex: +// +// Return values: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// Return values: +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +// +// +func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// +func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewExternalIPAddress: +func (client *WANPPPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway2/internetgateway2.go b/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway2/internetgateway2.go new file mode 100644 index 000000000..cf34c61f0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/dcps/internetgateway2/internetgateway2.go @@ -0,0 +1,5271 @@ +// Client for UPnP Device Control Protocol Internet Gateway Device v2. +// +// This DCP is documented in detail at: http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf +// +// Typically, use one of the New* functions to discover services on the local +// network. +package internetgateway2 + +// Generated file - do not edit by hand. See README.md + +import ( + "time" + + "github.com/fjl/goupnp" + "github.com/fjl/goupnp/soap" +) + +// Hack to avoid Go complaining if time isn't used. +var _ time.Time + +// Device URNs: +const ( + URN_LANDevice_1 = "urn:schemas-upnp-org:device:LANDevice:1" + URN_WANConnectionDevice_1 = "urn:schemas-upnp-org:device:WANConnectionDevice:1" + URN_WANConnectionDevice_2 = "urn:schemas-upnp-org:device:WANConnectionDevice:2" + URN_WANDevice_1 = "urn:schemas-upnp-org:device:WANDevice:1" + URN_WANDevice_2 = "urn:schemas-upnp-org:device:WANDevice:2" +) + +// Service URNs: +const ( + URN_LANHostConfigManagement_1 = "urn:schemas-upnp-org:service:LANHostConfigManagement:1" + URN_Layer3Forwarding_1 = "urn:schemas-upnp-org:service:Layer3Forwarding:1" + URN_WANCableLinkConfig_1 = "urn:schemas-upnp-org:service:WANCableLinkConfig:1" + URN_WANCommonInterfaceConfig_1 = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" + URN_WANDSLLinkConfig_1 = "urn:schemas-upnp-org:service:WANDSLLinkConfig:1" + URN_WANEthernetLinkConfig_1 = "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" + URN_WANIPConnection_1 = "urn:schemas-upnp-org:service:WANIPConnection:1" + URN_WANIPConnection_2 = "urn:schemas-upnp-org:service:WANIPConnection:2" + URN_WANIPv6FirewallControl_1 = "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" + URN_WANPOTSLinkConfig_1 = "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1" + URN_WANPPPConnection_1 = "urn:schemas-upnp-org:service:WANPPPConnection:1" +) + +// LANHostConfigManagement1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:LANHostConfigManagement:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type LANHostConfigManagement1 struct { + goupnp.ServiceClient +} + +// NewLANHostConfigManagement1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLANHostConfigManagement1Clients() (clients []*LANHostConfigManagement1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_LANHostConfigManagement_1); err != nil { + return + } + clients = make([]*LANHostConfigManagement1, len(genericClients)) + for i := range genericClients { + clients[i] = &LANHostConfigManagement1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewDHCPServerConfigurable: +// +// +func (client *LANHostConfigManagement1) SetDHCPServerConfigurable(NewDHCPServerConfigurable bool) (err error) { + // Request structure. + request := &struct { + NewDHCPServerConfigurable string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPServerConfigurable, err = soap.MarshalBoolean(NewDHCPServerConfigurable); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDHCPServerConfigurable: +func (client *LANHostConfigManagement1) GetDHCPServerConfigurable() (NewDHCPServerConfigurable bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPServerConfigurable string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPServerConfigurable, err = soap.UnmarshalBoolean(response.NewDHCPServerConfigurable); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDHCPRelay: +// +// +func (client *LANHostConfigManagement1) SetDHCPRelay(NewDHCPRelay bool) (err error) { + // Request structure. + request := &struct { + NewDHCPRelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPRelay, err = soap.MarshalBoolean(NewDHCPRelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDHCPRelay: +func (client *LANHostConfigManagement1) GetDHCPRelay() (NewDHCPRelay bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPRelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPRelay, err = soap.UnmarshalBoolean(response.NewDHCPRelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewSubnetMask: +// +// +func (client *LANHostConfigManagement1) SetSubnetMask(NewSubnetMask string) (err error) { + // Request structure. + request := &struct { + NewSubnetMask string + }{} + // BEGIN Marshal arguments into request. + + if request.NewSubnetMask, err = soap.MarshalString(NewSubnetMask); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewSubnetMask: +func (client *LANHostConfigManagement1) GetSubnetMask() (NewSubnetMask string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewSubnetMask string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewSubnetMask, err = soap.UnmarshalString(response.NewSubnetMask); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIPRouters: +// +// +func (client *LANHostConfigManagement1) SetIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIPRouters: +// +// +func (client *LANHostConfigManagement1) DeleteIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIPRouters: +func (client *LANHostConfigManagement1) GetIPRoutersList() (NewIPRouters string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIPRouters string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetIPRoutersList", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIPRouters, err = soap.UnmarshalString(response.NewIPRouters); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDomainName: +// +// +func (client *LANHostConfigManagement1) SetDomainName(NewDomainName string) (err error) { + // Request structure. + request := &struct { + NewDomainName string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDomainName, err = soap.MarshalString(NewDomainName); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDomainName: +func (client *LANHostConfigManagement1) GetDomainName() (NewDomainName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDomainName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDomainName, err = soap.UnmarshalString(response.NewDomainName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewMinAddress: +// +// * NewMaxAddress: +// +// +func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, NewMaxAddress string) (err error) { + // Request structure. + request := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewMinAddress, err = soap.MarshalString(NewMinAddress); err != nil { + return + } + if request.NewMaxAddress, err = soap.MarshalString(NewMaxAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewMinAddress: +// +// * NewMaxAddress: +func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string, NewMaxAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMinAddress, err = soap.UnmarshalString(response.NewMinAddress); err != nil { + return + } + if NewMaxAddress, err = soap.UnmarshalString(response.NewMaxAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewReservedAddresses: +// +// +func (client *LANHostConfigManagement1) SetReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewReservedAddresses: +// +// +func (client *LANHostConfigManagement1) DeleteReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewReservedAddresses: +func (client *LANHostConfigManagement1) GetReservedAddresses() (NewReservedAddresses string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedAddresses string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetReservedAddresses", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedAddresses, err = soap.UnmarshalString(response.NewReservedAddresses); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDNSServers: +// +// +func (client *LANHostConfigManagement1) SetDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDNSServers: +// +// +func (client *LANHostConfigManagement1) DeleteDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDNSServers: +func (client *LANHostConfigManagement1) GetDNSServers() (NewDNSServers string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDNSServers string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDNSServers", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDNSServers, err = soap.UnmarshalString(response.NewDNSServers); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Layer3Forwarding1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:Layer3Forwarding:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type Layer3Forwarding1 struct { + goupnp.ServiceClient +} + +// NewLayer3Forwarding1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLayer3Forwarding1Clients() (clients []*Layer3Forwarding1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_Layer3Forwarding_1); err != nil { + return + } + clients = make([]*Layer3Forwarding1, len(genericClients)) + for i := range genericClients { + clients[i] = &Layer3Forwarding1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewDefaultConnectionService: +// +// +func (client *Layer3Forwarding1) SetDefaultConnectionService(NewDefaultConnectionService string) (err error) { + // Request structure. + request := &struct { + NewDefaultConnectionService string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDefaultConnectionService, err = soap.MarshalString(NewDefaultConnectionService); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "SetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDefaultConnectionService: +func (client *Layer3Forwarding1) GetDefaultConnectionService() (NewDefaultConnectionService string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDefaultConnectionService string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "GetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDefaultConnectionService, err = soap.UnmarshalString(response.NewDefaultConnectionService); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCableLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCableLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCableLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCableLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCableLinkConfig1Clients() (clients []*WANCableLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCableLinkConfig_1); err != nil { + return + } + clients = make([]*WANCableLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCableLinkConfig1{genericClients[i]} + } + return +} + +// +// +// Return values: +// +// * NewCableLinkConfigState: allowed values: notReady, dsSyncComplete, usParamAcquired, rangingComplete, ipComplete, todEstablished, paramTransferComplete, registrationComplete, operational, accessDenied +// +// * NewLinkType: allowed values: Ethernet +func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigState string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewCableLinkConfigState string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetCableLinkConfigInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewCableLinkConfigState, err = soap.UnmarshalString(response.NewCableLinkConfigState); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDownstreamFrequency: +func (client *WANCableLinkConfig1) GetDownstreamFrequency() (NewDownstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamFrequency, err = soap.UnmarshalUi4(response.NewDownstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDownstreamModulation: allowed values: 64QAM, 256QAM +func (client *WANCableLinkConfig1) GetDownstreamModulation() (NewDownstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamModulation, err = soap.UnmarshalString(response.NewDownstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamFrequency: +func (client *WANCableLinkConfig1) GetUpstreamFrequency() (NewUpstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamFrequency, err = soap.UnmarshalUi4(response.NewUpstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamModulation: allowed values: QPSK, 16QAM +func (client *WANCableLinkConfig1) GetUpstreamModulation() (NewUpstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamModulation, err = soap.UnmarshalString(response.NewUpstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamChannelID: +func (client *WANCableLinkConfig1) GetUpstreamChannelID() (NewUpstreamChannelID uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamChannelID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamChannelID", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamChannelID, err = soap.UnmarshalUi4(response.NewUpstreamChannelID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamPowerLevel: +func (client *WANCableLinkConfig1) GetUpstreamPowerLevel() (NewUpstreamPowerLevel uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamPowerLevel string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamPowerLevel", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamPowerLevel, err = soap.UnmarshalUi4(response.NewUpstreamPowerLevel); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewBPIEncryptionEnabled: +func (client *WANCableLinkConfig1) GetBPIEncryptionEnabled() (NewBPIEncryptionEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewBPIEncryptionEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetBPIEncryptionEnabled", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewBPIEncryptionEnabled, err = soap.UnmarshalBoolean(response.NewBPIEncryptionEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConfigFile: +func (client *WANCableLinkConfig1) GetConfigFile() (NewConfigFile string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConfigFile string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetConfigFile", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConfigFile, err = soap.UnmarshalString(response.NewConfigFile); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTFTPServer: +func (client *WANCableLinkConfig1) GetTFTPServer() (NewTFTPServer string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTFTPServer string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetTFTPServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTFTPServer, err = soap.UnmarshalString(response.NewTFTPServer); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCommonInterfaceConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCommonInterfaceConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCommonInterfaceConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCommonInterfaceConfig1Clients() (clients []*WANCommonInterfaceConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCommonInterfaceConfig_1); err != nil { + return + } + clients = make([]*WANCommonInterfaceConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCommonInterfaceConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewEnabledForInternet: +// +// +func (client *WANCommonInterfaceConfig1) SetEnabledForInternet(NewEnabledForInternet bool) (err error) { + // Request structure. + request := &struct { + NewEnabledForInternet string + }{} + // BEGIN Marshal arguments into request. + + if request.NewEnabledForInternet, err = soap.MarshalBoolean(NewEnabledForInternet); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "SetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewEnabledForInternet: +func (client *WANCommonInterfaceConfig1) GetEnabledForInternet() (NewEnabledForInternet bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEnabledForInternet string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEnabledForInternet, err = soap.UnmarshalBoolean(response.NewEnabledForInternet); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWANAccessType: allowed values: DSL, POTS, Cable, Ethernet +// +// * NewLayer1UpstreamMaxBitRate: +// +// * NewLayer1DownstreamMaxBitRate: +// +// * NewPhysicalLinkStatus: allowed values: Up, Down +func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccessType string, NewLayer1UpstreamMaxBitRate uint32, NewLayer1DownstreamMaxBitRate uint32, NewPhysicalLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessType string + + NewLayer1UpstreamMaxBitRate string + + NewLayer1DownstreamMaxBitRate string + + NewPhysicalLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetCommonLinkProperties", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessType, err = soap.UnmarshalString(response.NewWANAccessType); err != nil { + return + } + if NewLayer1UpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1UpstreamMaxBitRate); err != nil { + return + } + if NewLayer1DownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1DownstreamMaxBitRate); err != nil { + return + } + if NewPhysicalLinkStatus, err = soap.UnmarshalString(response.NewPhysicalLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWANAccessProvider: +func (client *WANCommonInterfaceConfig1) GetWANAccessProvider() (NewWANAccessProvider string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessProvider string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetWANAccessProvider", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessProvider, err = soap.UnmarshalString(response.NewWANAccessProvider); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewMaximumActiveConnections: allowed value range: minimum=1, step=1 +func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaximumActiveConnections uint16, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMaximumActiveConnections string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetMaximumActiveConnections", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMaximumActiveConnections, err = soap.UnmarshalUi2(response.NewMaximumActiveConnections); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalBytesSent: +func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalBytesReceived: +func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalPacketsSent: +func (client *WANCommonInterfaceConfig1) GetTotalPacketsSent() (NewTotalPacketsSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsSent, err = soap.UnmarshalUi4(response.NewTotalPacketsSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewTotalPacketsReceived: +func (client *WANCommonInterfaceConfig1) GetTotalPacketsReceived() (NewTotalPacketsReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsReceived, err = soap.UnmarshalUi4(response.NewTotalPacketsReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewActiveConnectionIndex: +// +// Return values: +// +// * NewActiveConnDeviceContainer: +// +// * NewActiveConnectionServiceID: +func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnectionIndex uint16) (NewActiveConnDeviceContainer string, NewActiveConnectionServiceID string, err error) { + // Request structure. + request := &struct { + NewActiveConnectionIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewActiveConnectionIndex, err = soap.MarshalUi2(NewActiveConnectionIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewActiveConnDeviceContainer string + + NewActiveConnectionServiceID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetActiveConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewActiveConnDeviceContainer, err = soap.UnmarshalString(response.NewActiveConnDeviceContainer); err != nil { + return + } + if NewActiveConnectionServiceID, err = soap.UnmarshalString(response.NewActiveConnectionServiceID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANDSLLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANDSLLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANDSLLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANDSLLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANDSLLinkConfig1Clients() (clients []*WANDSLLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANDSLLinkConfig_1); err != nil { + return + } + clients = make([]*WANDSLLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANDSLLinkConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewLinkType: +// +// +func (client *WANDSLLinkConfig1) SetDSLLinkType(NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDSLLinkType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewLinkType: +// +// * NewLinkStatus: allowed values: Up, Down +func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewLinkType string + + NewLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDSLLinkInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + if NewLinkStatus, err = soap.UnmarshalString(response.NewLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoConfig: +func (client *WANDSLLinkConfig1) GetAutoConfig() (NewAutoConfig bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoConfig string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetAutoConfig", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoConfig, err = soap.UnmarshalBoolean(response.NewAutoConfig); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewModulationType: +func (client *WANDSLLinkConfig1) GetModulationType() (NewModulationType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewModulationType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetModulationType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewModulationType, err = soap.UnmarshalString(response.NewModulationType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewDestinationAddress: +// +// +func (client *WANDSLLinkConfig1) SetDestinationAddress(NewDestinationAddress string) (err error) { + // Request structure. + request := &struct { + NewDestinationAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDestinationAddress, err = soap.MarshalString(NewDestinationAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDestinationAddress: +func (client *WANDSLLinkConfig1) GetDestinationAddress() (NewDestinationAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDestinationAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDestinationAddress, err = soap.UnmarshalString(response.NewDestinationAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewATMEncapsulation: +// +// +func (client *WANDSLLinkConfig1) SetATMEncapsulation(NewATMEncapsulation string) (err error) { + // Request structure. + request := &struct { + NewATMEncapsulation string + }{} + // BEGIN Marshal arguments into request. + + if request.NewATMEncapsulation, err = soap.MarshalString(NewATMEncapsulation); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewATMEncapsulation: +func (client *WANDSLLinkConfig1) GetATMEncapsulation() (NewATMEncapsulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewATMEncapsulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewATMEncapsulation, err = soap.UnmarshalString(response.NewATMEncapsulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewFCSPreserved: +// +// +func (client *WANDSLLinkConfig1) SetFCSPreserved(NewFCSPreserved bool) (err error) { + // Request structure. + request := &struct { + NewFCSPreserved string + }{} + // BEGIN Marshal arguments into request. + + if request.NewFCSPreserved, err = soap.MarshalBoolean(NewFCSPreserved); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewFCSPreserved: +func (client *WANDSLLinkConfig1) GetFCSPreserved() (NewFCSPreserved bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFCSPreserved string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFCSPreserved, err = soap.UnmarshalBoolean(response.NewFCSPreserved); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANEthernetLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANEthernetLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANEthernetLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANEthernetLinkConfig1Clients() (clients []*WANEthernetLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANEthernetLinkConfig_1); err != nil { + return + } + clients = make([]*WANEthernetLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANEthernetLinkConfig1{genericClients[i]} + } + return +} + +// +// +// Return values: +// +// * NewEthernetLinkStatus: allowed values: Up, Down +func (client *WANEthernetLinkConfig1) GetEthernetLinkStatus() (NewEthernetLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEthernetLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANEthernetLinkConfig_1, "GetEthernetLinkStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEthernetLinkStatus, err = soap.UnmarshalString(response.NewEthernetLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection1Clients() (clients []*WANIPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_1); err != nil { + return + } + clients = make([]*WANIPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewConnectionType: +// +// +func (client *WANIPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionType: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, IP_Bridged +func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewAutoDisconnectTime: +// +// +func (client *WANIPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIdleDisconnectTime: +// +// +func (client *WANIPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewWarnDisconnectDelay: +// +// +func (client *WANIPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +// +// * NewUptime: +func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoDisconnectTime: +func (client *WANIPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIdleDisconnectTime: +func (client *WANIPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWarnDisconnectDelay: +func (client *WANIPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewRSIPAvailable: +// +// * NewNATEnabled: +func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewPortMappingIndex: +// +// Return values: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// Return values: +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +// +// +func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// +func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewExternalIPAddress: +func (client *WANIPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection2 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:2". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection2 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection2Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection2Clients() (clients []*WANIPConnection2, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_2); err != nil { + return + } + clients = make([]*WANIPConnection2, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection2{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewConnectionType: allowed values: Unconfigured, IP_Routed, IP_Bridged +// +// +func (client *WANIPConnection2) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionType: allowed values: Unconfigured, IP_Routed, IP_Bridged +// +// * NewPossibleConnectionTypes: +func (client *WANIPConnection2) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection2) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection2) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANIPConnection2) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewAutoDisconnectTime: +// +// +func (client *WANIPConnection2) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIdleDisconnectTime: +// +// +func (client *WANIPConnection2) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewWarnDisconnectDelay: +// +// +func (client *WANIPConnection2) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +// +// * NewUptime: +func (client *WANIPConnection2) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoDisconnectTime: +func (client *WANIPConnection2) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIdleDisconnectTime: +func (client *WANIPConnection2) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWarnDisconnectDelay: +func (client *WANIPConnection2) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewRSIPAvailable: +// +// * NewNATEnabled: +func (client *WANIPConnection2) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewPortMappingIndex: +// +// Return values: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: allowed value range: minimum=1, maximum=65535 +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: allowed value range: minimum=0, maximum=604800 +func (client *WANIPConnection2) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// Return values: +// +// * NewInternalPort: allowed value range: minimum=1, maximum=65535 +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: allowed value range: minimum=0, maximum=604800 +func (client *WANIPConnection2) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: allowed value range: minimum=1, maximum=65535 +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: allowed value range: minimum=0, maximum=604800 +// +// +func (client *WANIPConnection2) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// +func (client *WANIPConnection2) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewStartPort: +// +// * NewEndPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewManage: +// +// +func (client *WANIPConnection2) DeletePortMappingRange(NewStartPort uint16, NewEndPort uint16, NewProtocol string, NewManage bool) (err error) { + // Request structure. + request := &struct { + NewStartPort string + + NewEndPort string + + NewProtocol string + + NewManage string + }{} + // BEGIN Marshal arguments into request. + + if request.NewStartPort, err = soap.MarshalUi2(NewStartPort); err != nil { + return + } + if request.NewEndPort, err = soap.MarshalUi2(NewEndPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewManage, err = soap.MarshalBoolean(NewManage); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "DeletePortMappingRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewExternalIPAddress: +func (client *WANIPConnection2) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewStartPort: +// +// * NewEndPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewManage: +// +// * NewNumberOfPorts: +// +// Return values: +// +// * NewPortListing: +func (client *WANIPConnection2) GetListOfPortMappings(NewStartPort uint16, NewEndPort uint16, NewProtocol string, NewManage bool, NewNumberOfPorts uint16) (NewPortListing string, err error) { + // Request structure. + request := &struct { + NewStartPort string + + NewEndPort string + + NewProtocol string + + NewManage string + + NewNumberOfPorts string + }{} + // BEGIN Marshal arguments into request. + + if request.NewStartPort, err = soap.MarshalUi2(NewStartPort); err != nil { + return + } + if request.NewEndPort, err = soap.MarshalUi2(NewEndPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewManage, err = soap.MarshalBoolean(NewManage); err != nil { + return + } + if request.NewNumberOfPorts, err = soap.MarshalUi2(NewNumberOfPorts); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPortListing string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetListOfPortMappings", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPortListing, err = soap.UnmarshalString(response.NewPortListing); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: allowed value range: minimum=1, maximum=65535 +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: allowed value range: minimum=0, maximum=604800 +// +// Return values: +// +// * NewReservedPort: +func (client *WANIPConnection2) AddAnyPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (NewReservedPort uint16, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedPort string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "AddAnyPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedPort, err = soap.UnmarshalUi2(response.NewReservedPort); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPv6FirewallControl1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPv6FirewallControl1 struct { + goupnp.ServiceClient +} + +// NewWANIPv6FirewallControl1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPv6FirewallControl1Clients() (clients []*WANIPv6FirewallControl1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPv6FirewallControl_1); err != nil { + return + } + clients = make([]*WANIPv6FirewallControl1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPv6FirewallControl1{genericClients[i]} + } + return +} + +// +// +// Return values: +// +// * FirewallEnabled: +// +// * InboundPinholeAllowed: +func (client *WANIPv6FirewallControl1) GetFirewallStatus() (FirewallEnabled bool, InboundPinholeAllowed bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + FirewallEnabled string + + InboundPinholeAllowed string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetFirewallStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if FirewallEnabled, err = soap.UnmarshalBoolean(response.FirewallEnabled); err != nil { + return + } + if InboundPinholeAllowed, err = soap.UnmarshalBoolean(response.InboundPinholeAllowed); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * RemoteHost: +// +// * RemotePort: +// +// * InternalClient: +// +// * InternalPort: +// +// * Protocol: +// +// Return values: +// +// * OutboundPinholeTimeout: +func (client *WANIPv6FirewallControl1) GetOutboundPinholeTimeout(RemoteHost string, RemotePort uint16, InternalClient string, InternalPort uint16, Protocol uint16) (OutboundPinholeTimeout uint32, err error) { + // Request structure. + request := &struct { + RemoteHost string + + RemotePort string + + InternalClient string + + InternalPort string + + Protocol string + }{} + // BEGIN Marshal arguments into request. + + if request.RemoteHost, err = soap.MarshalString(RemoteHost); err != nil { + return + } + if request.RemotePort, err = soap.MarshalUi2(RemotePort); err != nil { + return + } + if request.InternalClient, err = soap.MarshalString(InternalClient); err != nil { + return + } + if request.InternalPort, err = soap.MarshalUi2(InternalPort); err != nil { + return + } + if request.Protocol, err = soap.MarshalUi2(Protocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + OutboundPinholeTimeout string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetOutboundPinholeTimeout", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if OutboundPinholeTimeout, err = soap.UnmarshalUi4(response.OutboundPinholeTimeout); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * RemoteHost: +// +// * RemotePort: +// +// * InternalClient: +// +// * InternalPort: +// +// * Protocol: +// +// * LeaseTime: allowed value range: minimum=1, maximum=86400 +// +// Return values: +// +// * UniqueID: +func (client *WANIPv6FirewallControl1) AddPinhole(RemoteHost string, RemotePort uint16, InternalClient string, InternalPort uint16, Protocol uint16, LeaseTime uint32) (UniqueID uint16, err error) { + // Request structure. + request := &struct { + RemoteHost string + + RemotePort string + + InternalClient string + + InternalPort string + + Protocol string + + LeaseTime string + }{} + // BEGIN Marshal arguments into request. + + if request.RemoteHost, err = soap.MarshalString(RemoteHost); err != nil { + return + } + if request.RemotePort, err = soap.MarshalUi2(RemotePort); err != nil { + return + } + if request.InternalClient, err = soap.MarshalString(InternalClient); err != nil { + return + } + if request.InternalPort, err = soap.MarshalUi2(InternalPort); err != nil { + return + } + if request.Protocol, err = soap.MarshalUi2(Protocol); err != nil { + return + } + if request.LeaseTime, err = soap.MarshalUi4(LeaseTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + UniqueID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "AddPinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if UniqueID, err = soap.UnmarshalUi2(response.UniqueID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * UniqueID: +// +// * NewLeaseTime: allowed value range: minimum=1, maximum=86400 +// +// +func (client *WANIPv6FirewallControl1) UpdatePinhole(UniqueID uint16, NewLeaseTime uint32) (err error) { + // Request structure. + request := &struct { + UniqueID string + + NewLeaseTime string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + if request.NewLeaseTime, err = soap.MarshalUi4(NewLeaseTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "UpdatePinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * UniqueID: +// +// +func (client *WANIPv6FirewallControl1) DeletePinhole(UniqueID uint16) (err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "DeletePinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * UniqueID: +// +// Return values: +// +// * PinholePackets: +func (client *WANIPv6FirewallControl1) GetPinholePackets(UniqueID uint16) (PinholePackets uint32, err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + PinholePackets string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetPinholePackets", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if PinholePackets, err = soap.UnmarshalUi4(response.PinholePackets); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * UniqueID: +// +// Return values: +// +// * IsWorking: +func (client *WANIPv6FirewallControl1) CheckPinholeWorking(UniqueID uint16) (IsWorking bool, err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + IsWorking string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "CheckPinholeWorking", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if IsWorking, err = soap.UnmarshalBoolean(response.IsWorking); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPOTSLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPOTSLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANPOTSLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPOTSLinkConfig1Clients() (clients []*WANPOTSLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPOTSLinkConfig_1); err != nil { + return + } + clients = make([]*WANPOTSLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPOTSLinkConfig1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewISPPhoneNumber: +// +// * NewISPInfo: +// +// * NewLinkType: allowed values: PPP_Dialup +// +// +func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInfo string, NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewISPPhoneNumber, err = soap.MarshalString(NewISPPhoneNumber); err != nil { + return + } + if request.NewISPInfo, err = soap.MarshalString(NewISPInfo); err != nil { + return + } + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewNumberOfRetries: +// +// * NewDelayBetweenRetries: +// +// +func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, NewDelayBetweenRetries uint32) (err error) { + // Request structure. + request := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + // BEGIN Marshal arguments into request. + + if request.NewNumberOfRetries, err = soap.MarshalUi4(NewNumberOfRetries); err != nil { + return + } + if request.NewDelayBetweenRetries, err = soap.MarshalUi4(NewDelayBetweenRetries); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewISPPhoneNumber: +// +// * NewISPInfo: +// +// * NewLinkType: allowed values: PPP_Dialup +func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISPInfo string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewISPPhoneNumber, err = soap.UnmarshalString(response.NewISPPhoneNumber); err != nil { + return + } + if NewISPInfo, err = soap.UnmarshalString(response.NewISPInfo); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewNumberOfRetries: +// +// * NewDelayBetweenRetries: +func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32, NewDelayBetweenRetries uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewNumberOfRetries, err = soap.UnmarshalUi4(response.NewNumberOfRetries); err != nil { + return + } + if NewDelayBetweenRetries, err = soap.UnmarshalUi4(response.NewDelayBetweenRetries); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewFclass: +func (client *WANPOTSLinkConfig1) GetFclass() (NewFclass string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFclass string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetFclass", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFclass, err = soap.UnmarshalString(response.NewFclass); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataModulationSupported: +func (client *WANPOTSLinkConfig1) GetDataModulationSupported() (NewDataModulationSupported string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataModulationSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataModulationSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataModulationSupported, err = soap.UnmarshalString(response.NewDataModulationSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataProtocol: +func (client *WANPOTSLinkConfig1) GetDataProtocol() (NewDataProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataProtocol, err = soap.UnmarshalString(response.NewDataProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewDataCompression: +func (client *WANPOTSLinkConfig1) GetDataCompression() (NewDataCompression string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataCompression string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataCompression", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataCompression, err = soap.UnmarshalString(response.NewDataCompression); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPlusVTRCommandSupported: +func (client *WANPOTSLinkConfig1) GetPlusVTRCommandSupported() (NewPlusVTRCommandSupported bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPlusVTRCommandSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetPlusVTRCommandSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPlusVTRCommandSupported, err = soap.UnmarshalBoolean(response.NewPlusVTRCommandSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPPPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPPPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPPPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANPPPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPPPConnection1Clients() (clients []*WANPPPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPPPConnection_1); err != nil { + return + } + clients = make([]*WANPPPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPPPConnection1{genericClients[i]} + } + return +} + +// Arguments: +// +// * NewConnectionType: +// +// +func (client *WANPPPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionType: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, DHCP_Spoofed, PPPoE_Bridged, PPTP_Relay, L2TP_Relay, PPPoE_Relay +func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewUserName: +// +// * NewPassword: +// +// +func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPassword string) (err error) { + // Request structure. + request := &struct { + NewUserName string + + NewPassword string + }{} + // BEGIN Marshal arguments into request. + + if request.NewUserName, err = soap.MarshalString(NewUserName); err != nil { + return + } + if request.NewPassword, err = soap.MarshalString(NewPassword); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ConfigureConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// +func (client *WANPPPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewAutoDisconnectTime: +// +// +func (client *WANPPPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewIdleDisconnectTime: +// +// +func (client *WANPPPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewWarnDisconnectDelay: +// +// +func (client *WANPPPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +// +// * NewUptime: +func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUpstreamMaxBitRate: +// +// * NewDownstreamMaxBitRate: +func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRate uint32, NewDownstreamMaxBitRate uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamMaxBitRate string + + NewDownstreamMaxBitRate string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetLinkLayerMaxBitRates", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewUpstreamMaxBitRate); err != nil { + return + } + if NewDownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewDownstreamMaxBitRate); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPEncryptionProtocol: +func (client *WANPPPConnection1) GetPPPEncryptionProtocol() (NewPPPEncryptionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPEncryptionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPEncryptionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPEncryptionProtocol, err = soap.UnmarshalString(response.NewPPPEncryptionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPCompressionProtocol: +func (client *WANPPPConnection1) GetPPPCompressionProtocol() (NewPPPCompressionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPCompressionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPCompressionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPCompressionProtocol, err = soap.UnmarshalString(response.NewPPPCompressionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPPPAuthenticationProtocol: +func (client *WANPPPConnection1) GetPPPAuthenticationProtocol() (NewPPPAuthenticationProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPAuthenticationProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPAuthenticationProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPAuthenticationProtocol, err = soap.UnmarshalString(response.NewPPPAuthenticationProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewUserName: +func (client *WANPPPConnection1) GetUserName() (NewUserName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUserName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetUserName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUserName, err = soap.UnmarshalString(response.NewUserName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewPassword: +func (client *WANPPPConnection1) GetPassword() (NewPassword string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPassword string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPassword", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPassword, err = soap.UnmarshalString(response.NewPassword); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewAutoDisconnectTime: +func (client *WANPPPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewIdleDisconnectTime: +func (client *WANPPPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewWarnDisconnectDelay: +func (client *WANPPPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewRSIPAvailable: +// +// * NewNATEnabled: +func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewPortMappingIndex: +// +// Return values: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// Return values: +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// * NewInternalPort: +// +// * NewInternalClient: +// +// * NewEnabled: +// +// * NewPortMappingDescription: +// +// * NewLeaseDuration: +// +// +func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// Arguments: +// +// * NewRemoteHost: +// +// * NewExternalPort: +// +// * NewProtocol: allowed values: TCP, UDP +// +// +func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// +// Return values: +// +// * NewExternalIPAddress: +func (client *WANPPPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/device.go b/Godeps/_workspace/src/github.com/fjl/goupnp/device.go new file mode 100644 index 000000000..2b8e9bb07 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/device.go @@ -0,0 +1,184 @@ +// This file contains XML structures for communicating with UPnP devices. + +package goupnp + +import ( + "encoding/xml" + "errors" + "fmt" + "net/url" + + "github.com/fjl/goupnp/scpd" + "github.com/fjl/goupnp/soap" +) + +const ( + DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0" +) + +// RootDevice is the device description as described by section 2.3 "Device +// description" in +// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf +type RootDevice struct { + XMLName xml.Name `xml:"root"` + SpecVersion SpecVersion `xml:"specVersion"` + URLBase url.URL `xml:"-"` + URLBaseStr string `xml:"URLBase"` + Device Device `xml:"device"` +} + +// SetURLBase sets the URLBase for the RootDevice and its underlying components. +func (root *RootDevice) SetURLBase(urlBase *url.URL) { + root.URLBase = *urlBase + root.URLBaseStr = urlBase.String() + root.Device.SetURLBase(urlBase) +} + +// SpecVersion is part of a RootDevice, describes the version of the +// specification that the data adheres to. +type SpecVersion struct { + Major int32 `xml:"major"` + Minor int32 `xml:"minor"` +} + +// Device is a UPnP device. It can have child devices. +type Device struct { + DeviceType string `xml:"deviceType"` + FriendlyName string `xml:"friendlyName"` + Manufacturer string `xml:"manufacturer"` + ManufacturerURL URLField `xml:"manufacturerURL"` + ModelDescription string `xml:"modelDescription"` + ModelName string `xml:"modelName"` + ModelNumber string `xml:"modelNumber"` + ModelURL URLField `xml:"modelURL"` + SerialNumber string `xml:"serialNumber"` + UDN string `xml:"UDN"` + UPC string `xml:"UPC,omitempty"` + Icons []Icon `xml:"iconList>icon,omitempty"` + Services []Service `xml:"serviceList>service,omitempty"` + Devices []Device `xml:"deviceList>device,omitempty"` + + // Extra observed elements: + PresentationURL URLField `xml:"presentationURL"` +} + +// VisitDevices calls visitor for the device, and all its descendent devices. +func (device *Device) VisitDevices(visitor func(*Device)) { + visitor(device) + for i := range device.Devices { + device.Devices[i].VisitDevices(visitor) + } +} + +// VisitServices calls visitor for all Services under the device and all its +// descendent devices. +func (device *Device) VisitServices(visitor func(*Service)) { + device.VisitDevices(func(d *Device) { + for i := range d.Services { + visitor(&d.Services[i]) + } + }) +} + +// FindService finds all (if any) Services under the device and its descendents +// that have the given ServiceType. +func (device *Device) FindService(serviceType string) []*Service { + var services []*Service + device.VisitServices(func(s *Service) { + if s.ServiceType == serviceType { + services = append(services, s) + } + }) + return services +} + +// SetURLBase sets the URLBase for the Device and its underlying components. +func (device *Device) SetURLBase(urlBase *url.URL) { + device.ManufacturerURL.SetURLBase(urlBase) + device.ModelURL.SetURLBase(urlBase) + device.PresentationURL.SetURLBase(urlBase) + for i := range device.Icons { + device.Icons[i].SetURLBase(urlBase) + } + for i := range device.Services { + device.Services[i].SetURLBase(urlBase) + } + for i := range device.Devices { + device.Devices[i].SetURLBase(urlBase) + } +} + +func (device *Device) String() string { + return fmt.Sprintf("Device ID %s : %s (%s)", device.UDN, device.DeviceType, device.FriendlyName) +} + +// Icon is a representative image that a device might include in its +// description. +type Icon struct { + Mimetype string `xml:"mimetype"` + Width int32 `xml:"width"` + Height int32 `xml:"height"` + Depth int32 `xml:"depth"` + URL URLField `xml:"url"` +} + +// SetURLBase sets the URLBase for the Icon. +func (icon *Icon) SetURLBase(url *url.URL) { + icon.URL.SetURLBase(url) +} + +// Service is a service provided by a UPnP Device. +type Service struct { + ServiceType string `xml:"serviceType"` + ServiceId string `xml:"serviceId"` + SCPDURL URLField `xml:"SCPDURL"` + ControlURL URLField `xml:"controlURL"` + EventSubURL URLField `xml:"eventSubURL"` +} + +// SetURLBase sets the URLBase for the Service. +func (srv *Service) SetURLBase(urlBase *url.URL) { + srv.SCPDURL.SetURLBase(urlBase) + srv.ControlURL.SetURLBase(urlBase) + srv.EventSubURL.SetURLBase(urlBase) +} + +func (srv *Service) String() string { + return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType) +} + +// RequestSCDP requests the SCPD (soap actions and state variables description) +// for the service. +func (srv *Service) RequestSCDP() (*scpd.SCPD, error) { + if !srv.SCPDURL.Ok { + return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set") + } + s := new(scpd.SCPD) + if err := requestXml(srv.SCPDURL.URL.String(), scpd.SCPDXMLNamespace, s); err != nil { + return nil, err + } + return s, nil +} + +func (srv *Service) NewSOAPClient() *soap.SOAPClient { + return soap.NewSOAPClient(srv.ControlURL.URL) +} + +// URLField is a URL that is part of a device description. +type URLField struct { + URL url.URL `xml:"-"` + Ok bool `xml:"-"` + Str string `xml:",chardata"` +} + +func (uf *URLField) SetURLBase(urlBase *url.URL) { + refUrl, err := url.Parse(uf.Str) + if err != nil { + uf.URL = url.URL{} + uf.Ok = false + return + } + + uf.URL = *urlBase.ResolveReference(refUrl) + uf.Ok = true +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/example/example.go b/Godeps/_workspace/src/github.com/fjl/goupnp/example/example.go new file mode 100644 index 000000000..aae4c28f4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/example/example.go @@ -0,0 +1,6 @@ +// Serves as examples of using the goupnp library. +// +// To run examples and see the output for your local network, run the following +// command (specifically including the -v flag): +// go test -v github.com/fjl/goupnp/example +package example diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/example/example_test.go b/Godeps/_workspace/src/github.com/fjl/goupnp/example/example_test.go new file mode 100644 index 000000000..d926a06b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/example/example_test.go @@ -0,0 +1,62 @@ +package example_test + +import ( + "fmt" + "os" + + "github.com/fjl/goupnp" + "github.com/fjl/goupnp/dcps/internetgateway1" +) + +// Use discovered WANPPPConnection1 services to find external IP addresses. +func Example_WANPPPConnection1_GetExternalIPAddress() { + clients, errors, err := internetgateway1.NewWANPPPConnection1Clients() + extIPClients := make([]GetExternalIPAddresser, len(clients)) + for i, client := range clients { + extIPClients[i] = client + } + DisplayExternalIPResults(extIPClients, errors, err) + // Output: +} + +// Use discovered WANIPConnection services to find external IP addresses. +func Example_WANIPConnection_GetExternalIPAddress() { + clients, errors, err := internetgateway1.NewWANIPConnection1Clients() + extIPClients := make([]GetExternalIPAddresser, len(clients)) + for i, client := range clients { + extIPClients[i] = client + } + DisplayExternalIPResults(extIPClients, errors, err) + // Output: +} + +type GetExternalIPAddresser interface { + GetExternalIPAddress() (NewExternalIPAddress string, err error) + GetServiceClient() *goupnp.ServiceClient +} + +func DisplayExternalIPResults(clients []GetExternalIPAddresser, errors []error, err error) { + if err != nil { + fmt.Fprintln(os.Stderr, "Error discovering service with UPnP: ", err) + return + } + + if len(errors) > 0 { + fmt.Fprintf(os.Stderr, "Error discovering %d services:\n", len(errors)) + for _, err := range errors { + fmt.Println(" ", err) + } + } + + fmt.Fprintf(os.Stderr, "Successfully discovered %d services:\n", len(clients)) + for _, client := range clients { + device := &client.GetServiceClient().RootDevice.Device + + fmt.Fprintln(os.Stderr, " Device:", device.FriendlyName) + if addr, err := client.GetExternalIPAddress(); err != nil { + fmt.Fprintf(os.Stderr, " Failed to get external IP address: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, " External IP address: %v\n", addr) + } + } +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/gotasks/specgen_task.go b/Godeps/_workspace/src/github.com/fjl/goupnp/gotasks/specgen_task.go new file mode 100644 index 000000000..006e8fef3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/gotasks/specgen_task.go @@ -0,0 +1,539 @@ +// +build gotask + +package gotasks + +import ( + "archive/zip" + "bytes" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "regexp" + "strings" + "text/template" + + "github.com/fjl/goupnp" + "github.com/fjl/goupnp/scpd" + "github.com/huin/goutil/codegen" + "github.com/jingweno/gotask/tasking" +) + +var ( + deviceURNPrefix = "urn:schemas-upnp-org:device:" + serviceURNPrefix = "urn:schemas-upnp-org:service:" +) + +// NAME +// specgen - generates Go code from the UPnP specification files. +// +// DESCRIPTION +// The specification is available for download from: +// +// OPTIONS +// -s, --spec_filename= +// Path to the specification file, available from http://upnp.org/resources/upnpresources.zip +// -o, --out_dir= +// Path to the output directory. This is is where the DCP source files will be placed. Should normally correspond to the directory for github.com/fjl/goupnp/dcps +// --nogofmt +// Disable passing the output through gofmt. Do this if debugging code output problems and needing to see the generated code prior to being passed through gofmt. +func TaskSpecgen(t *tasking.T) { + specFilename := t.Flags.String("spec-filename") + if specFilename == "" { + specFilename = t.Flags.String("s") + } + if specFilename == "" { + t.Fatal("--spec_filename is required") + } + outDir := t.Flags.String("out-dir") + if outDir == "" { + outDir = t.Flags.String("o") + } + if outDir == "" { + log.Fatal("--out_dir is required") + } + useGofmt := !t.Flags.Bool("nogofmt") + + specArchive, err := openZipfile(specFilename) + if err != nil { + t.Fatalf("Error opening spec file: %v", err) + } + defer specArchive.Close() + + dcpCol := newDcpsCollection() + for _, f := range globFiles("standardizeddcps/*/*.zip", specArchive.Reader) { + dirName := strings.TrimPrefix(f.Name, "standardizeddcps/") + slashIndex := strings.Index(dirName, "/") + if slashIndex == -1 { + // Should not happen. + t.Logf("Could not find / in %q", dirName) + return + } + dirName = dirName[:slashIndex] + + dcp := dcpCol.dcpForDir(dirName) + if dcp == nil { + t.Logf("No alias defined for directory %q: skipping %s\n", dirName, f.Name) + continue + } else { + t.Logf("Alias found for directory %q: processing %s\n", dirName, f.Name) + } + + dcp.processZipFile(f) + } + + for _, dcp := range dcpCol.dcpByAlias { + if err := dcp.writePackage(outDir, useGofmt); err != nil { + log.Printf("Error writing package %q: %v", dcp.Metadata.Name, err) + } + } +} + +// DCP contains extra metadata to use when generating DCP source files. +type DCPMetadata struct { + Name string // What to name the Go DCP package. + OfficialName string // Official name for the DCP. + DocURL string // Optional - URL for futher documentation about the DCP. +} + +var dcpMetadataByDir = map[string]DCPMetadata{ + "Internet Gateway_1": { + Name: "internetgateway1", + OfficialName: "Internet Gateway Device v1", + DocURL: "http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf", + }, + "Internet Gateway_2": { + Name: "internetgateway2", + OfficialName: "Internet Gateway Device v2", + DocURL: "http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf", + }, +} + +type dcpCollection struct { + dcpByAlias map[string]*DCP +} + +func newDcpsCollection() *dcpCollection { + c := &dcpCollection{ + dcpByAlias: make(map[string]*DCP), + } + for _, metadata := range dcpMetadataByDir { + c.dcpByAlias[metadata.Name] = newDCP(metadata) + } + return c +} + +func (c *dcpCollection) dcpForDir(dirName string) *DCP { + metadata, ok := dcpMetadataByDir[dirName] + if !ok { + return nil + } + return c.dcpByAlias[metadata.Name] +} + +// DCP collects together information about a UPnP Device Control Protocol. +type DCP struct { + Metadata DCPMetadata + DeviceTypes map[string]*URNParts + ServiceTypes map[string]*URNParts + Services []SCPDWithURN +} + +func newDCP(metadata DCPMetadata) *DCP { + return &DCP{ + Metadata: metadata, + DeviceTypes: make(map[string]*URNParts), + ServiceTypes: make(map[string]*URNParts), + } +} + +func (dcp *DCP) processZipFile(file *zip.File) { + archive, err := openChildZip(file) + if err != nil { + log.Println("Error reading child zip file:", err) + return + } + for _, deviceFile := range globFiles("*/device/*.xml", archive) { + dcp.processDeviceFile(deviceFile) + } + for _, scpdFile := range globFiles("*/service/*.xml", archive) { + dcp.processSCPDFile(scpdFile) + } +} + +func (dcp *DCP) processDeviceFile(file *zip.File) { + var device goupnp.Device + if err := unmarshalXmlFile(file, &device); err != nil { + log.Printf("Error decoding device XML from file %q: %v", file.Name, err) + return + } + device.VisitDevices(func(d *goupnp.Device) { + t := strings.TrimSpace(d.DeviceType) + if t != "" { + u, err := extractURNParts(t, deviceURNPrefix) + if err != nil { + log.Println(err) + return + } + dcp.DeviceTypes[t] = u + } + }) + device.VisitServices(func(s *goupnp.Service) { + u, err := extractURNParts(s.ServiceType, serviceURNPrefix) + if err != nil { + log.Println(err) + return + } + dcp.ServiceTypes[s.ServiceType] = u + }) +} + +func (dcp *DCP) writePackage(outDir string, useGofmt bool) error { + packageDirname := filepath.Join(outDir, dcp.Metadata.Name) + err := os.MkdirAll(packageDirname, os.ModePerm) + if err != nil && !os.IsExist(err) { + return err + } + packageFilename := filepath.Join(packageDirname, dcp.Metadata.Name+".go") + packageFile, err := os.Create(packageFilename) + if err != nil { + return err + } + var output io.WriteCloser = packageFile + if useGofmt { + if output, err = codegen.NewGofmtWriteCloser(output); err != nil { + packageFile.Close() + return err + } + } + if err = packageTmpl.Execute(output, dcp); err != nil { + output.Close() + return err + } + return output.Close() +} + +func (dcp *DCP) processSCPDFile(file *zip.File) { + scpd := new(scpd.SCPD) + if err := unmarshalXmlFile(file, scpd); err != nil { + log.Printf("Error decoding SCPD XML from file %q: %v", file.Name, err) + return + } + scpd.Clean() + urnParts, err := urnPartsFromSCPDFilename(file.Name) + if err != nil { + log.Printf("Could not recognize SCPD filename %q: %v", file.Name, err) + return + } + dcp.Services = append(dcp.Services, SCPDWithURN{ + URNParts: urnParts, + SCPD: scpd, + }) +} + +type SCPDWithURN struct { + *URNParts + SCPD *scpd.SCPD +} + +func (s *SCPDWithURN) WrapArgument(arg scpd.Argument) (*argumentWrapper, error) { + relVar := s.SCPD.GetStateVariable(arg.RelatedStateVariable) + if relVar == nil { + return nil, fmt.Errorf("no such state variable: %q, for argument %q", arg.RelatedStateVariable, arg.Name) + } + cnv, ok := typeConvs[relVar.DataType.Name] + if !ok { + return nil, fmt.Errorf("unknown data type: %q, for state variable %q, for argument %q", relVar.DataType.Type, arg.RelatedStateVariable, arg.Name) + } + return &argumentWrapper{ + Argument: arg, + relVar: relVar, + conv: cnv, + }, nil +} + +type argumentWrapper struct { + scpd.Argument + relVar *scpd.StateVariable + conv conv +} + +func (arg *argumentWrapper) AsParameter() string { + return fmt.Sprintf("%s %s", arg.Name, arg.conv.ExtType) +} + +func (arg *argumentWrapper) Document() string { + relVar := arg.relVar + if rng := relVar.AllowedValueRange; rng != nil { + var parts []string + if rng.Minimum != "" { + parts = append(parts, fmt.Sprintf("minimum=%s", rng.Minimum)) + } + if rng.Maximum != "" { + parts = append(parts, fmt.Sprintf("maximum=%s", rng.Maximum)) + } + if rng.Step != "" { + parts = append(parts, fmt.Sprintf("step=%s", rng.Step)) + } + return "allowed value range: " + strings.Join(parts, ", ") + } + if len(relVar.AllowedValues) != 0 { + return "allowed values: " + strings.Join(relVar.AllowedValues, ", ") + } + return "" +} + +func (arg *argumentWrapper) Marshal() string { + return fmt.Sprintf("soap.Marshal%s(%s)", arg.conv.FuncSuffix, arg.Name) +} + +func (arg *argumentWrapper) Unmarshal(objVar string) string { + return fmt.Sprintf("soap.Unmarshal%s(%s.%s)", arg.conv.FuncSuffix, objVar, arg.Name) +} + +type conv struct { + FuncSuffix string + ExtType string +} + +// typeConvs maps from a SOAP type (e.g "fixed.14.4") to the function name +// suffix inside the soap module (e.g "Fixed14_4") and the Go type. +var typeConvs = map[string]conv{ + "ui1": conv{"Ui1", "uint8"}, + "ui2": conv{"Ui2", "uint16"}, + "ui4": conv{"Ui4", "uint32"}, + "i1": conv{"I1", "int8"}, + "i2": conv{"I2", "int16"}, + "i4": conv{"I4", "int32"}, + "int": conv{"Int", "int64"}, + "r4": conv{"R4", "float32"}, + "r8": conv{"R8", "float64"}, + "number": conv{"R8", "float64"}, // Alias for r8. + "fixed.14.4": conv{"Fixed14_4", "float64"}, + "float": conv{"R8", "float64"}, + "char": conv{"Char", "rune"}, + "string": conv{"String", "string"}, + "date": conv{"Date", "time.Time"}, + "dateTime": conv{"DateTime", "time.Time"}, + "dateTime.tz": conv{"DateTimeTz", "time.Time"}, + "time": conv{"TimeOfDay", "soap.TimeOfDay"}, + "time.tz": conv{"TimeOfDayTz", "soap.TimeOfDay"}, + "boolean": conv{"Boolean", "bool"}, + "bin.base64": conv{"BinBase64", "[]byte"}, + "bin.hex": conv{"BinHex", "[]byte"}, +} + +type closeableZipReader struct { + io.Closer + *zip.Reader +} + +func openZipfile(filename string) (*closeableZipReader, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + fi, err := file.Stat() + if err != nil { + return nil, err + } + archive, err := zip.NewReader(file, fi.Size()) + if err != nil { + return nil, err + } + return &closeableZipReader{ + Closer: file, + Reader: archive, + }, nil +} + +// openChildZip opens a zip file within another zip file. +func openChildZip(file *zip.File) (*zip.Reader, error) { + zipFile, err := file.Open() + if err != nil { + return nil, err + } + defer zipFile.Close() + + zipBytes, err := ioutil.ReadAll(zipFile) + if err != nil { + return nil, err + } + + return zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) +} + +func globFiles(pattern string, archive *zip.Reader) []*zip.File { + var files []*zip.File + for _, f := range archive.File { + if matched, err := path.Match(pattern, f.Name); err != nil { + // This shouldn't happen - all patterns are hard-coded, errors in them + // are a programming error. + panic(err) + } else if matched { + files = append(files, f) + } + } + return files +} + +func unmarshalXmlFile(file *zip.File, data interface{}) error { + r, err := file.Open() + if err != nil { + return err + } + decoder := xml.NewDecoder(r) + r.Close() + return decoder.Decode(data) +} + +type URNParts struct { + URN string + Name string + Version string +} + +func (u *URNParts) Const() string { + return fmt.Sprintf("URN_%s_%s", u.Name, u.Version) +} + +// extractURNParts extracts the name and version from a URN string. +func extractURNParts(urn, expectedPrefix string) (*URNParts, error) { + if !strings.HasPrefix(urn, expectedPrefix) { + return nil, fmt.Errorf("%q does not have expected prefix %q", urn, expectedPrefix) + } + parts := strings.SplitN(strings.TrimPrefix(urn, expectedPrefix), ":", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("%q does not have a name and version", urn) + } + name, version := parts[0], parts[1] + return &URNParts{urn, name, version}, nil +} + +var scpdFilenameRe = regexp.MustCompile( + `.*/([a-zA-Z0-9]+)([0-9]+)\.xml`) + +func urnPartsFromSCPDFilename(filename string) (*URNParts, error) { + parts := scpdFilenameRe.FindStringSubmatch(filename) + if len(parts) != 3 { + return nil, fmt.Errorf("SCPD filename %q does not have expected number of parts", filename) + } + name, version := parts[1], parts[2] + return &URNParts{ + URN: serviceURNPrefix + name + ":" + version, + Name: name, + Version: version, + }, nil +} + +var packageTmpl = template.Must(template.New("package").Parse(`{{$name := .Metadata.Name}} +// Client for UPnP Device Control Protocol {{.Metadata.OfficialName}}. +// {{if .Metadata.DocURL}} +// This DCP is documented in detail at: {{.Metadata.DocURL}}{{end}} +// +// Typically, use one of the New* functions to discover services on the local +// network. +package {{$name}} + +// Generated file - do not edit by hand. See README.md + + +import ( + "time" + + "github.com/fjl/goupnp" + "github.com/fjl/goupnp/soap" +) + +// Hack to avoid Go complaining if time isn't used. +var _ time.Time + +// Device URNs: +const ({{range .DeviceTypes}} + {{.Const}} = "{{.URN}}"{{end}} +) + +// Service URNs: +const ({{range .ServiceTypes}} + {{.Const}} = "{{.URN}}"{{end}} +) + +{{range .Services}} +{{$srv := .}} +{{$srvIdent := printf "%s%s" .Name .Version}} + +// {{$srvIdent}} is a client for UPnP SOAP service with URN "{{.URN}}". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type {{$srvIdent}} struct { + goupnp.ServiceClient +} + +// New{{$srvIdent}}Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func New{{$srvIdent}}Clients() (clients []*{{$srvIdent}}, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients({{$srv.Const}}); err != nil { + return + } + clients = make([]*{{$srvIdent}}, len(genericClients)) + for i := range genericClients { + clients[i] = &{{$srvIdent}}{genericClients[i]} + } + return +} + +{{range .SCPD.Actions}}{{/* loops over *SCPDWithURN values */}} + +{{$inargs := .InputArguments}}{{$outargs := .OutputArguments}} +// {{if $inargs}}Arguments:{{range $inargs}}{{$argWrap := $srv.WrapArgument .}} +// +// * {{.Name}}: {{$argWrap.Document}}{{end}}{{end}} +// +// {{if $outargs}}Return values:{{range $outargs}}{{$argWrap := $srv.WrapArgument .}} +// +// * {{.Name}}: {{$argWrap.Document}}{{end}}{{end}} +func (client *{{$srvIdent}}) {{.Name}}({{range $inargs}}{{/* +*/}}{{$argWrap := $srv.WrapArgument .}}{{$argWrap.AsParameter}}, {{end}}{{/* +*/}}) ({{range $outargs}}{{/* +*/}}{{$argWrap := $srv.WrapArgument .}}{{$argWrap.AsParameter}}, {{end}} err error) { + // Request structure. + request := {{if $inargs}}&{{template "argstruct" $inargs}}{{"{}"}}{{else}}{{"interface{}(nil)"}}{{end}} + // BEGIN Marshal arguments into request. +{{range $inargs}}{{$argWrap := $srv.WrapArgument .}} + if request.{{.Name}}, err = {{$argWrap.Marshal}}; err != nil { + return + }{{end}} + // END Marshal arguments into request. + + // Response structure. + response := {{if $outargs}}&{{template "argstruct" $outargs}}{{"{}"}}{{else}}{{"interface{}(nil)"}}{{end}} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction({{$srv.URNParts.Const}}, "{{.Name}}", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. +{{range $outargs}}{{$argWrap := $srv.WrapArgument .}} + if {{.Name}}, err = {{$argWrap.Unmarshal "response"}}; err != nil { + return + }{{end}} + // END Unmarshal arguments from response. + return +} +{{end}}{{/* range .SCPD.Actions */}} +{{end}}{{/* range .Services */}} + +{{define "argstruct"}}struct {{"{"}}{{range .}} +{{.Name}} string +{{end}}{{"}"}}{{end}} +`)) diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/goupnp.go b/Godeps/_workspace/src/github.com/fjl/goupnp/goupnp.go new file mode 100644 index 000000000..c962fbc57 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/goupnp.go @@ -0,0 +1,109 @@ +// goupnp is an implementation of a client for various UPnP services. +// +// For most uses, it is recommended to use the code-generated packages under +// github.com/fjl/goupnp/dcps. Example use is shown at +// http://godoc.org/github.com/fjl/goupnp/example +// +// A commonly used client is internetgateway1.WANPPPConnection1: +// http://godoc.org/github.com/fjl/goupnp/dcps/internetgateway1#WANPPPConnection1 +// +// Currently only a couple of schemas have code generated for them from the +// UPnP example XML specifications. Not all methods will work on these clients, +// because the generated stubs contain the full set of specified methods from +// the XML specifications, and the discovered services will likely support a +// subset of those methods. +package goupnp + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + + "github.com/fjl/goupnp/httpu" + "github.com/fjl/goupnp/ssdp" +) + +// ContextError is an error that wraps an error with some context information. +type ContextError struct { + Context string + Err error +} + +func (err ContextError) Error() string { + return fmt.Sprintf("%s: %v", err.Context, err.Err) +} + +// MaybeRootDevice contains either a RootDevice or an error. +type MaybeRootDevice struct { + Root *RootDevice + Err error +} + +// DiscoverDevices attempts to find targets of the given type. This is +// typically the entry-point for this package. searchTarget is typically a URN +// in the form "urn:schemas-upnp-org:device:..." or +// "urn:schemas-upnp-org:service:...". A single error is returned for errors +// while attempting to send the query. An error or RootDevice is returned for +// each discovered RootDevice. +func DiscoverDevices(searchTarget string) ([]MaybeRootDevice, error) { + httpu, err := httpu.NewHTTPUClient() + if err != nil { + return nil, err + } + defer httpu.Close() + responses, err := ssdp.SSDPRawSearch(httpu, string(searchTarget), 2, 3) + if err != nil { + return nil, err + } + + results := make([]MaybeRootDevice, len(responses)) + for i, response := range responses { + maybe := &results[i] + loc, err := response.Location() + if err != nil { + + maybe.Err = ContextError{"unexpected bad location from search", err} + continue + } + locStr := loc.String() + root := new(RootDevice) + if err := requestXml(locStr, DeviceXMLNamespace, root); err != nil { + maybe.Err = ContextError{fmt.Sprintf("error requesting root device details from %q", locStr), err} + continue + } + var urlBaseStr string + if root.URLBaseStr != "" { + urlBaseStr = root.URLBaseStr + } else { + urlBaseStr = locStr + } + urlBase, err := url.Parse(urlBaseStr) + if err != nil { + maybe.Err = ContextError{fmt.Sprintf("error parsing location URL %q", locStr), err} + continue + } + root.SetURLBase(urlBase) + maybe.Root = root + } + + return results, nil +} + +func requestXml(url string, defaultSpace string, doc interface{}) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("goupnp: got response status %s from %q", + resp.Status, url) + } + + decoder := xml.NewDecoder(resp.Body) + decoder.DefaultSpace = defaultSpace + + return decoder.Decode(doc) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/httpu.go b/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/httpu.go new file mode 100644 index 000000000..862c3def4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/httpu.go @@ -0,0 +1,117 @@ +package httpu + +import ( + "bufio" + "bytes" + "fmt" + "log" + "net" + "net/http" + "sync" + "time" +) + +// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical +// function is for HTTPMU, and particularly SSDP. +type HTTPUClient struct { + connLock sync.Mutex // Protects use of conn. + conn net.PacketConn +} + +// NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the +// purpose. +func NewHTTPUClient() (*HTTPUClient, error) { + conn, err := net.ListenPacket("udp", ":0") + if err != nil { + return nil, err + } + return &HTTPUClient{conn: conn}, nil +} + +// Close shuts down the client. The client will no longer be useful following +// this. +func (httpu *HTTPUClient) Close() error { + httpu.connLock.Lock() + defer httpu.connLock.Unlock() + return httpu.conn.Close() +} + +// Do performs a request. The timeout is how long to wait for before returning +// the responses that were received. An error is only returned for failing to +// send the request. Failures in receipt simply do not add to the resulting +// responses. +// +// Note that at present only one concurrent connection will happen per +// HTTPUClient. +func (httpu *HTTPUClient) Do(req *http.Request, timeout time.Duration, numSends int) ([]*http.Response, error) { + httpu.connLock.Lock() + defer httpu.connLock.Unlock() + + // Create the request. This is a subset of what http.Request.Write does + // deliberately to avoid creating extra fields which may confuse some + // devices. + var requestBuf bytes.Buffer + method := req.Method + if method == "" { + method = "GET" + } + if _, err := fmt.Fprintf(&requestBuf, "%s %s HTTP/1.1\r\n", method, req.URL.RequestURI()); err != nil { + return nil, err + } + if err := req.Header.Write(&requestBuf); err != nil { + return nil, err + } + if _, err := requestBuf.Write([]byte{'\r', '\n'}); err != nil { + return nil, err + } + + destAddr, err := net.ResolveUDPAddr("udp", req.Host) + if err != nil { + return nil, err + } + if err = httpu.conn.SetDeadline(time.Now().Add(timeout)); err != nil { + return nil, err + } + + // Send request. + for i := 0; i < numSends; i++ { + if n, err := httpu.conn.WriteTo(requestBuf.Bytes(), destAddr); err != nil { + return nil, err + } else if n < len(requestBuf.Bytes()) { + return nil, fmt.Errorf("httpu: wrote %d bytes rather than full %d in request", + n, len(requestBuf.Bytes())) + } + time.Sleep(5 * time.Millisecond) + } + + // Await responses until timeout. + var responses []*http.Response + responseBytes := make([]byte, 2048) + for { + // 2048 bytes should be sufficient for most networks. + n, _, err := httpu.conn.ReadFrom(responseBytes) + if err != nil { + if err, ok := err.(net.Error); ok { + if err.Timeout() { + break + } + if err.Temporary() { + // Sleep in case this is a persistent error to avoid pegging CPU until deadline. + time.Sleep(10 * time.Millisecond) + continue + } + } + return nil, err + } + + // Parse response. + response, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(responseBytes[:n])), req) + if err != nil { + log.Print("httpu: error while parsing response: %v", err) + continue + } + + responses = append(responses, response) + } + return responses, err +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/serve.go b/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/serve.go new file mode 100644 index 000000000..9f67af85b --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/httpu/serve.go @@ -0,0 +1,108 @@ +package httpu + +import ( + "bufio" + "bytes" + "log" + "net" + "net/http" + "regexp" +) + +const ( + DefaultMaxMessageBytes = 2048 +) + +var ( + trailingWhitespaceRx = regexp.MustCompile(" +\r\n") + crlf = []byte("\r\n") +) + +// Handler is the interface by which received HTTPU messages are passed to +// handling code. +type Handler interface { + // ServeMessage is called for each HTTPU message received. peerAddr contains + // the address that the message was received from. + ServeMessage(r *http.Request) +} + +// HandlerFunc is a function-to-Handler adapter. +type HandlerFunc func(r *http.Request) + +func (f HandlerFunc) ServeMessage(r *http.Request) { + f(r) +} + +// A Server defines parameters for running an HTTPU server. +type Server struct { + Addr string // UDP address to listen on + Multicast bool // Should listen for multicast? + Interface *net.Interface // Network interface to listen on for multicast, nil for default multicast interface + Handler Handler // handler to invoke + MaxMessageBytes int // maximum number of bytes to read from a packet, DefaultMaxMessageBytes if 0 +} + +// ListenAndServe listens on the UDP network address srv.Addr. If srv.Multicast +// is true, then a multicast UDP listener will be used on srv.Interface (or +// default interface if nil). +func (srv *Server) ListenAndServe() error { + var err error + + var addr *net.UDPAddr + if addr, err = net.ResolveUDPAddr("udp", srv.Addr); err != nil { + log.Fatal(err) + } + + var conn net.PacketConn + if srv.Multicast { + if conn, err = net.ListenMulticastUDP("udp", srv.Interface, addr); err != nil { + return err + } + } else { + if conn, err = net.ListenUDP("udp", addr); err != nil { + return err + } + } + + return srv.Serve(conn) +} + +// Serve messages received on the given packet listener to the srv.Handler. +func (srv *Server) Serve(l net.PacketConn) error { + maxMessageBytes := DefaultMaxMessageBytes + if srv.MaxMessageBytes != 0 { + maxMessageBytes = srv.MaxMessageBytes + } + for { + buf := make([]byte, maxMessageBytes) + n, peerAddr, err := l.ReadFrom(buf) + if err != nil { + return err + } + buf = buf[:n] + + go func(buf []byte, peerAddr net.Addr) { + // At least one router's UPnP implementation has added a trailing space + // after "HTTP/1.1" - trim it. + buf = trailingWhitespaceRx.ReplaceAllLiteral(buf, crlf) + + req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf))) + if err != nil { + log.Printf("httpu: Failed to parse request: %v", err) + return + } + req.RemoteAddr = peerAddr.String() + srv.Handler.ServeMessage(req) + // No need to call req.Body.Close - underlying reader is bytes.Buffer. + }(buf, peerAddr) + } +} + +// Serve messages received on the given packet listener to the given handler. +func Serve(l net.PacketConn, handler Handler) error { + srv := Server{ + Handler: handler, + MaxMessageBytes: DefaultMaxMessageBytes, + } + return srv.Serve(l) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/scpd/scpd.go b/Godeps/_workspace/src/github.com/fjl/goupnp/scpd/scpd.go new file mode 100644 index 000000000..c9d2e69e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/scpd/scpd.go @@ -0,0 +1,167 @@ +package scpd + +import ( + "encoding/xml" + "strings" +) + +const ( + SCPDXMLNamespace = "urn:schemas-upnp-org:service-1-0" +) + +func cleanWhitespace(s *string) { + *s = strings.TrimSpace(*s) +} + +// SCPD is the service description as described by section 2.5 "Service +// description" in +// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf +type SCPD struct { + XMLName xml.Name `xml:"scpd"` + ConfigId string `xml:"configId,attr"` + SpecVersion SpecVersion `xml:"specVersion"` + Actions []Action `xml:"actionList>action"` + StateVariables []StateVariable `xml:"serviceStateTable>stateVariable"` +} + +// Clean attempts to remove stray whitespace etc. in the structure. It seems +// unfortunately common for stray whitespace to be present in SCPD documents, +// this method attempts to make it easy to clean them out. +func (scpd *SCPD) Clean() { + cleanWhitespace(&scpd.ConfigId) + for i := range scpd.Actions { + scpd.Actions[i].clean() + } + for i := range scpd.StateVariables { + scpd.StateVariables[i].clean() + } +} + +func (scpd *SCPD) GetStateVariable(variable string) *StateVariable { + for i := range scpd.StateVariables { + v := &scpd.StateVariables[i] + if v.Name == variable { + return v + } + } + return nil +} + +func (scpd *SCPD) GetAction(action string) *Action { + for i := range scpd.Actions { + a := &scpd.Actions[i] + if a.Name == action { + return a + } + } + return nil +} + +// SpecVersion is part of a SCPD document, describes the version of the +// specification that the data adheres to. +type SpecVersion struct { + Major int32 `xml:"major"` + Minor int32 `xml:"minor"` +} + +type Action struct { + Name string `xml:"name"` + Arguments []Argument `xml:"argumentList>argument"` +} + +func (action *Action) clean() { + cleanWhitespace(&action.Name) + for i := range action.Arguments { + action.Arguments[i].clean() + } +} + +func (action *Action) InputArguments() []*Argument { + var result []*Argument + for i := range action.Arguments { + arg := &action.Arguments[i] + if arg.IsInput() { + result = append(result, arg) + } + } + return result +} + +func (action *Action) OutputArguments() []*Argument { + var result []*Argument + for i := range action.Arguments { + arg := &action.Arguments[i] + if arg.IsOutput() { + result = append(result, arg) + } + } + return result +} + +type Argument struct { + Name string `xml:"name"` + Direction string `xml:"direction"` // in|out + RelatedStateVariable string `xml:"relatedStateVariable"` // ? + Retval string `xml:"retval"` // ? +} + +func (arg *Argument) clean() { + cleanWhitespace(&arg.Name) + cleanWhitespace(&arg.Direction) + cleanWhitespace(&arg.RelatedStateVariable) + cleanWhitespace(&arg.Retval) +} + +func (arg *Argument) IsInput() bool { + return arg.Direction == "in" +} + +func (arg *Argument) IsOutput() bool { + return arg.Direction == "out" +} + +type StateVariable struct { + Name string `xml:"name"` + SendEvents string `xml:"sendEvents,attr"` // yes|no + Multicast string `xml:"multicast,attr"` // yes|no + DataType DataType `xml:"dataType"` + DefaultValue string `xml:"defaultValue"` + AllowedValueRange *AllowedValueRange `xml:"allowedValueRange"` + AllowedValues []string `xml:"allowedValueList>allowedValue"` +} + +func (v *StateVariable) clean() { + cleanWhitespace(&v.Name) + cleanWhitespace(&v.SendEvents) + cleanWhitespace(&v.Multicast) + v.DataType.clean() + cleanWhitespace(&v.DefaultValue) + if v.AllowedValueRange != nil { + v.AllowedValueRange.clean() + } + for i := range v.AllowedValues { + cleanWhitespace(&v.AllowedValues[i]) + } +} + +type AllowedValueRange struct { + Minimum string `xml:"minimum"` + Maximum string `xml:"maximum"` + Step string `xml:"step"` +} + +func (r *AllowedValueRange) clean() { + cleanWhitespace(&r.Minimum) + cleanWhitespace(&r.Maximum) + cleanWhitespace(&r.Step) +} + +type DataType struct { + Name string `xml:",chardata"` + Type string `xml:"type,attr"` +} + +func (dt *DataType) clean() { + cleanWhitespace(&dt.Name) + cleanWhitespace(&dt.Type) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/service_client.go b/Godeps/_workspace/src/github.com/fjl/goupnp/service_client.go new file mode 100644 index 000000000..258b7593b --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/service_client.go @@ -0,0 +1,57 @@ +package goupnp + +import ( + "fmt" + + "github.com/fjl/goupnp/soap" +) + +// ServiceClient is a SOAP client, root device and the service for the SOAP +// client rolled into one value. The root device and service are intended to be +// informational. +type ServiceClient struct { + SOAPClient *soap.SOAPClient + RootDevice *RootDevice + Service *Service +} + +func NewServiceClients(searchTarget string) (clients []ServiceClient, errors []error, err error) { + var maybeRootDevices []MaybeRootDevice + if maybeRootDevices, err = DiscoverDevices(searchTarget); err != nil { + return + } + + clients = make([]ServiceClient, 0, len(maybeRootDevices)) + + for _, maybeRootDevice := range maybeRootDevices { + if maybeRootDevice.Err != nil { + errors = append(errors, maybeRootDevice.Err) + continue + } + + device := &maybeRootDevice.Root.Device + srvs := device.FindService(searchTarget) + if len(srvs) == 0 { + errors = append(errors, fmt.Errorf("goupnp: service %q not found within device %q (UDN=%q)", + searchTarget, device.FriendlyName, device.UDN)) + continue + } + + for _, srv := range srvs { + clients = append(clients, ServiceClient{ + SOAPClient: srv.NewSOAPClient(), + RootDevice: maybeRootDevice.Root, + Service: srv, + }) + } + } + + return +} + +// GetServiceClient returns the ServiceClient itself. This is provided so that the +// service client attributes can be accessed via an interface method on a +// wrapping type. +func (client *ServiceClient) GetServiceClient() *ServiceClient { + return client +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap.go b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap.go new file mode 100644 index 000000000..815610734 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap.go @@ -0,0 +1,157 @@ +// Definition for the SOAP structure required for UPnP's SOAP usage. + +package soap + +import ( + "bytes" + "encoding/xml" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "reflect" +) + +const ( + soapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" + soapPrefix = xml.Header + `` + soapSuffix = `` +) + +type SOAPClient struct { + EndpointURL url.URL + HTTPClient http.Client +} + +func NewSOAPClient(endpointURL url.URL) *SOAPClient { + return &SOAPClient{ + EndpointURL: endpointURL, + } +} + +// PerformSOAPAction makes a SOAP request, with the given action. +// inAction and outAction must both be pointers to structs with string fields +// only. +func (client *SOAPClient) PerformAction(actionNamespace, actionName string, inAction interface{}, outAction interface{}) error { + requestBytes, err := encodeRequestAction(actionNamespace, actionName, inAction) + if err != nil { + return err + } + + response, err := client.HTTPClient.Do(&http.Request{ + Method: "POST", + URL: &client.EndpointURL, + Header: http.Header{ + "SOAPACTION": []string{`"` + actionNamespace + "#" + actionName + `"`}, + "CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)), + // Set ContentLength to avoid chunked encoding - some servers might not support it. + ContentLength: int64(len(requestBytes)), + }) + if err != nil { + return fmt.Errorf("goupnp: error performing SOAP HTTP request: %v", err) + } + defer response.Body.Close() + if response.StatusCode != 200 { + return fmt.Errorf("goupnp: SOAP request got HTTP %s", response.Status) + } + + responseEnv := newSOAPEnvelope() + decoder := xml.NewDecoder(response.Body) + if err := decoder.Decode(responseEnv); err != nil { + return fmt.Errorf("goupnp: error decoding response body: %v", err) + } + + if responseEnv.Body.Fault != nil { + return responseEnv.Body.Fault + } + + if outAction != nil { + if err := xml.Unmarshal(responseEnv.Body.RawAction, outAction); err != nil { + return fmt.Errorf("goupnp: error unmarshalling out action: %v, %v", err, responseEnv.Body.RawAction) + } + } + + return nil +} + +// newSOAPAction creates a soapEnvelope with the given action and arguments. +func newSOAPEnvelope() *soapEnvelope { + return &soapEnvelope{ + EncodingStyle: soapEncodingStyle, + } +} + +// encodeRequestAction is a hacky way to create an encoded SOAP envelope +// containing the given action. Experiments with one router have shown that it +// 500s for requests where the outer default xmlns is set to the SOAP +// namespace, and then reassigning the default namespace within that to the +// service namespace. Hand-coding the outer XML to work-around this. +func encodeRequestAction(actionNamespace, actionName string, inAction interface{}) ([]byte, error) { + requestBuf := new(bytes.Buffer) + requestBuf.WriteString(soapPrefix) + requestBuf.WriteString(``) + if inAction != nil { + if err := encodeRequestArgs(requestBuf, inAction); err != nil { + return nil, err + } + } + requestBuf.WriteString(``) + requestBuf.WriteString(soapSuffix) + return requestBuf.Bytes(), nil +} + +func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error { + in := reflect.Indirect(reflect.ValueOf(inAction)) + if in.Kind() != reflect.Struct { + return fmt.Errorf("goupnp: SOAP inAction is not a struct but of type %v", in.Type()) + } + enc := xml.NewEncoder(w) + nFields := in.NumField() + inType := in.Type() + for i := 0; i < nFields; i++ { + field := inType.Field(i) + argName := field.Name + if nameOverride := field.Tag.Get("soap"); nameOverride != "" { + argName = nameOverride + } + value := in.Field(i) + if value.Kind() != reflect.String { + return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type()) + } + if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil { + return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err) + } + } + enc.Flush() + return nil +} + +type soapEnvelope struct { + XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + EncodingStyle string `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"` + Body soapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` +} + +type soapBody struct { + Fault *SOAPFaultError `xml:"Fault"` + RawAction []byte `xml:",innerxml"` +} + +// SOAPFaultError implements error, and contains SOAP fault information. +type SOAPFaultError struct { + FaultCode string `xml:"faultcode"` + FaultString string `xml:"faultstring"` + Detail string `xml:"detail"` +} + +func (err *SOAPFaultError) Error() string { + return fmt.Sprintf("SOAP fault: %s", err.FaultString) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap_test.go b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap_test.go new file mode 100644 index 000000000..75dbbdbf1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/soap_test.go @@ -0,0 +1,85 @@ +package soap + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "testing" +) + +type capturingRoundTripper struct { + err error + resp *http.Response + capturedReq *http.Request +} + +func (rt *capturingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + rt.capturedReq = req + return rt.resp, rt.err +} + +func TestActionInputs(t *testing.T) { + url, err := url.Parse("http://example.com/soap") + if err != nil { + t.Fatal(err) + } + rt := &capturingRoundTripper{ + err: nil, + resp: &http.Response{ + StatusCode: 200, + Body: ioutil.NopCloser(bytes.NewBufferString(` + + + + valueA + valueB + + + + `)), + }, + } + client := SOAPClient{ + EndpointURL: *url, + HTTPClient: http.Client{ + Transport: rt, + }, + } + + type In struct { + Foo string + Bar string `soap:"bar"` + } + type Out struct { + A string + B string + } + in := In{"foo", "bar"} + gotOut := Out{} + err = client.PerformAction("mynamespace", "myaction", &in, &gotOut) + if err != nil { + t.Fatal(err) + } + + wantBody := (soapPrefix + + `` + + `foo` + + `bar` + + `` + + soapSuffix) + body, err := ioutil.ReadAll(rt.capturedReq.Body) + if err != nil { + t.Fatal(err) + } + gotBody := string(body) + if wantBody != gotBody { + t.Errorf("Bad request body\nwant: %q\n got: %q", wantBody, gotBody) + } + + wantOut := Out{"valueA", "valueB"} + if !reflect.DeepEqual(wantOut, gotOut) { + t.Errorf("Bad output\nwant: %+v\n got: %+v", wantOut, gotOut) + } +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types.go b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types.go new file mode 100644 index 000000000..cd16510e3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types.go @@ -0,0 +1,508 @@ +package soap + +import ( + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +var ( + // localLoc acts like time.Local for this package, but is faked out by the + // unit tests to ensure that things stay constant (especially when running + // this test in a place where local time is UTC which might mask bugs). + localLoc = time.Local +) + +func MarshalUi1(v uint8) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi1(s string) (uint8, error) { + v, err := strconv.ParseUint(s, 10, 8) + return uint8(v), err +} + +func MarshalUi2(v uint16) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi2(s string) (uint16, error) { + v, err := strconv.ParseUint(s, 10, 16) + return uint16(v), err +} + +func MarshalUi4(v uint32) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi4(s string) (uint32, error) { + v, err := strconv.ParseUint(s, 10, 32) + return uint32(v), err +} + +func MarshalI1(v int8) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI1(s string) (int8, error) { + v, err := strconv.ParseInt(s, 10, 8) + return int8(v), err +} + +func MarshalI2(v int16) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI2(s string) (int16, error) { + v, err := strconv.ParseInt(s, 10, 16) + return int16(v), err +} + +func MarshalI4(v int32) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI4(s string) (int32, error) { + v, err := strconv.ParseInt(s, 10, 32) + return int32(v), err +} + +func MarshalInt(v int64) (string, error) { + return strconv.FormatInt(v, 10), nil +} + +func UnmarshalInt(s string) (int64, error) { + return strconv.ParseInt(s, 10, 64) +} + +func MarshalR4(v float32) (string, error) { + return strconv.FormatFloat(float64(v), 'G', -1, 32), nil +} + +func UnmarshalR4(s string) (float32, error) { + v, err := strconv.ParseFloat(s, 32) + return float32(v), err +} + +func MarshalR8(v float64) (string, error) { + return strconv.FormatFloat(v, 'G', -1, 64), nil +} + +func UnmarshalR8(s string) (float64, error) { + v, err := strconv.ParseFloat(s, 64) + return float64(v), err +} + +// MarshalFixed14_4 marshals float64 to SOAP "fixed.14.4" type. +func MarshalFixed14_4(v float64) (string, error) { + if v >= 1e14 || v <= -1e14 { + return "", fmt.Errorf("soap fixed14.4: value %v out of bounds", v) + } + return strconv.FormatFloat(v, 'f', 4, 64), nil +} + +// UnmarshalFixed14_4 unmarshals float64 from SOAP "fixed.14.4" type. +func UnmarshalFixed14_4(s string) (float64, error) { + v, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, err + } + if v >= 1e14 || v <= -1e14 { + return 0, fmt.Errorf("soap fixed14.4: value %q out of bounds", s) + } + return v, nil +} + +// MarshalChar marshals rune to SOAP "char" type. +func MarshalChar(v rune) (string, error) { + if v == 0 { + return "", errors.New("soap char: rune 0 is not allowed") + } + return string(v), nil +} + +// UnmarshalChar unmarshals rune from SOAP "char" type. +func UnmarshalChar(s string) (rune, error) { + if len(s) == 0 { + return 0, errors.New("soap char: got empty string") + } + r, n := utf8.DecodeRune([]byte(s)) + if n != len(s) { + return 0, fmt.Errorf("soap char: value %q is not a single rune", s) + } + return r, nil +} + +func MarshalString(v string) (string, error) { + return v, nil +} + +func UnmarshalString(v string) (string, error) { + return v, nil +} + +func parseInt(s string, err *error) int { + v, parseErr := strconv.ParseInt(s, 10, 64) + if parseErr != nil { + *err = parseErr + } + return int(v) +} + +var dateRegexps = []*regexp.Regexp{ + // yyyy[-mm[-dd]] + regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`), + // yyyy[mm[dd]] + regexp.MustCompile(`^(\d{4})(?:(\d{2})(?:(\d{2}))?)?$`), +} + +func parseDateParts(s string) (year, month, day int, err error) { + var parts []string + for _, re := range dateRegexps { + parts = re.FindStringSubmatch(s) + if parts != nil { + break + } + } + if parts == nil { + err = fmt.Errorf("soap date: value %q is not in a recognized ISO8601 date format", s) + return + } + + year = parseInt(parts[1], &err) + month = 1 + day = 1 + if len(parts[2]) != 0 { + month = parseInt(parts[2], &err) + if len(parts[3]) != 0 { + day = parseInt(parts[3], &err) + } + } + + if err != nil { + err = fmt.Errorf("soap date: %q: %v", s, err) + } + + return +} + +var timeRegexps = []*regexp.Regexp{ + // hh[:mm[:ss]] + regexp.MustCompile(`^(\d{2})(?::(\d{2})(?::(\d{2}))?)?$`), + // hh[mm[ss]] + regexp.MustCompile(`^(\d{2})(?:(\d{2})(?:(\d{2}))?)?$`), +} + +func parseTimeParts(s string) (hour, minute, second int, err error) { + var parts []string + for _, re := range timeRegexps { + parts = re.FindStringSubmatch(s) + if parts != nil { + break + } + } + if parts == nil { + err = fmt.Errorf("soap time: value %q is not in ISO8601 time format", s) + return + } + + hour = parseInt(parts[1], &err) + if len(parts[2]) != 0 { + minute = parseInt(parts[2], &err) + if len(parts[3]) != 0 { + second = parseInt(parts[3], &err) + } + } + + if err != nil { + err = fmt.Errorf("soap time: %q: %v", s, err) + } + + return +} + +// (+|-)hh[[:]mm] +var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2})(?::?(\d{2}))?$`) + +func parseTimezone(s string) (offset int, err error) { + if s == "Z" { + return 0, nil + } + parts := timezoneRegexp.FindStringSubmatch(s) + if parts == nil { + err = fmt.Errorf("soap timezone: value %q is not in ISO8601 timezone format", s) + return + } + + offset = parseInt(parts[2], &err) * 3600 + if len(parts[3]) != 0 { + offset += parseInt(parts[3], &err) * 60 + } + if parts[1] == "-" { + offset = -offset + } + + if err != nil { + err = fmt.Errorf("soap timezone: %q: %v", s, err) + } + + return +} + +var completeDateTimeZoneRegexp = regexp.MustCompile(`^([^T]+)(?:T([^-+Z]+)(.+)?)?$`) + +// splitCompleteDateTimeZone splits date, time and timezone apart from an +// ISO8601 string. It does not ensure that the contents of each part are +// correct, it merely splits on certain delimiters. +// e.g "2010-09-08T12:15:10+0700" => "2010-09-08", "12:15:10", "+0700". +// Timezone can only be present if time is also present. +func splitCompleteDateTimeZone(s string) (dateStr, timeStr, zoneStr string, err error) { + parts := completeDateTimeZoneRegexp.FindStringSubmatch(s) + if parts == nil { + err = fmt.Errorf("soap date/time/zone: value %q is not in ISO8601 datetime format", s) + return + } + dateStr = parts[1] + timeStr = parts[2] + zoneStr = parts[3] + return +} + +// MarshalDate marshals time.Time to SOAP "date" type. Note that this converts +// to local time, and discards the time-of-day components. +func MarshalDate(v time.Time) (string, error) { + return v.In(localLoc).Format("2006-01-02"), nil +} + +var dateFmts = []string{"2006-01-02", "20060102"} + +// UnmarshalDate unmarshals time.Time from SOAP "date" type. This outputs the +// date as midnight in the local time zone. +func UnmarshalDate(s string) (time.Time, error) { + year, month, day, err := parseDateParts(s) + if err != nil { + return time.Time{}, err + } + return time.Date(year, time.Month(month), day, 0, 0, 0, 0, localLoc), nil +} + +// TimeOfDay is used in cases where SOAP "time" or "time.tz" is used. +type TimeOfDay struct { + // Duration of time since midnight. + FromMidnight time.Duration + + // Set to true if Offset is specified. If false, then the timezone is + // unspecified (and by ISO8601 - implies some "local" time). + HasOffset bool + + // Offset is non-zero only if time.tz is used. It is otherwise ignored. If + // non-zero, then it is regarded as a UTC offset in seconds. Note that the + // sub-minutes is ignored by the marshal function. + Offset int +} + +// MarshalTimeOfDay marshals TimeOfDay to the "time" type. +func MarshalTimeOfDay(v TimeOfDay) (string, error) { + d := int64(v.FromMidnight / time.Second) + hour := d / 3600 + d = d % 3600 + minute := d / 60 + second := d % 60 + + return fmt.Sprintf("%02d:%02d:%02d", hour, minute, second), nil +} + +// UnmarshalTimeOfDay unmarshals TimeOfDay from the "time" type. +func UnmarshalTimeOfDay(s string) (TimeOfDay, error) { + t, err := UnmarshalTimeOfDayTz(s) + if err != nil { + return TimeOfDay{}, err + } else if t.HasOffset { + return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone") + } + return t, nil +} + +// MarshalTimeOfDayTz marshals TimeOfDay to the "time.tz" type. +func MarshalTimeOfDayTz(v TimeOfDay) (string, error) { + d := int64(v.FromMidnight / time.Second) + hour := d / 3600 + d = d % 3600 + minute := d / 60 + second := d % 60 + + tz := "" + if v.HasOffset { + if v.Offset == 0 { + tz = "Z" + } else { + offsetMins := v.Offset / 60 + sign := '+' + if offsetMins < 1 { + offsetMins = -offsetMins + sign = '-' + } + tz = fmt.Sprintf("%c%02d:%02d", sign, offsetMins/60, offsetMins%60) + } + } + + return fmt.Sprintf("%02d:%02d:%02d%s", hour, minute, second, tz), nil +} + +// UnmarshalTimeOfDayTz unmarshals TimeOfDay from the "time.tz" type. +func UnmarshalTimeOfDayTz(s string) (tod TimeOfDay, err error) { + zoneIndex := strings.IndexAny(s, "Z+-") + var timePart string + var hasOffset bool + var offset int + if zoneIndex == -1 { + hasOffset = false + timePart = s + } else { + hasOffset = true + timePart = s[:zoneIndex] + if offset, err = parseTimezone(s[zoneIndex:]); err != nil { + return + } + } + + hour, minute, second, err := parseTimeParts(timePart) + if err != nil { + return + } + + fromMidnight := time.Duration(hour*3600+minute*60+second) * time.Second + + // ISO8601 special case - values up to 24:00:00 are allowed, so using + // strictly greater-than for the maximum value. + if fromMidnight > 24*time.Hour || minute >= 60 || second >= 60 { + return TimeOfDay{}, fmt.Errorf("soap time.tz: value %q has value(s) out of range", s) + } + + return TimeOfDay{ + FromMidnight: time.Duration(hour*3600+minute*60+second) * time.Second, + HasOffset: hasOffset, + Offset: offset, + }, nil +} + +// MarshalDateTime marshals time.Time to SOAP "dateTime" type. Note that this +// converts to local time. +func MarshalDateTime(v time.Time) (string, error) { + return v.In(localLoc).Format("2006-01-02T15:04:05"), nil +} + +// UnmarshalDateTime unmarshals time.Time from the SOAP "dateTime" type. This +// returns a value in the local timezone. +func UnmarshalDateTime(s string) (result time.Time, err error) { + dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) + if err != nil { + return + } + + if len(zoneStr) != 0 { + err = fmt.Errorf("soap datetime: unexpected timezone in %q", s) + return + } + + year, month, day, err := parseDateParts(dateStr) + if err != nil { + return + } + + var hour, minute, second int + if len(timeStr) != 0 { + hour, minute, second, err = parseTimeParts(timeStr) + if err != nil { + return + } + } + + result = time.Date(year, time.Month(month), day, hour, minute, second, 0, localLoc) + return +} + +// MarshalDateTimeTz marshals time.Time to SOAP "dateTime.tz" type. +func MarshalDateTimeTz(v time.Time) (string, error) { + return v.Format("2006-01-02T15:04:05-07:00"), nil +} + +// UnmarshalDateTimeTz unmarshals time.Time from the SOAP "dateTime.tz" type. +// This returns a value in the local timezone when the timezone is unspecified. +func UnmarshalDateTimeTz(s string) (result time.Time, err error) { + dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) + if err != nil { + return + } + + year, month, day, err := parseDateParts(dateStr) + if err != nil { + return + } + + var hour, minute, second int + var location *time.Location = localLoc + if len(timeStr) != 0 { + hour, minute, second, err = parseTimeParts(timeStr) + if err != nil { + return + } + if len(zoneStr) != 0 { + var offset int + offset, err = parseTimezone(zoneStr) + if offset == 0 { + location = time.UTC + } else { + location = time.FixedZone("", offset) + } + } + } + + result = time.Date(year, time.Month(month), day, hour, minute, second, 0, location) + return +} + +// MarshalBoolean marshals bool to SOAP "boolean" type. +func MarshalBoolean(v bool) (string, error) { + if v { + return "1", nil + } + return "0", nil +} + +// UnmarshalBoolean unmarshals bool from the SOAP "boolean" type. +func UnmarshalBoolean(s string) (bool, error) { + switch s { + case "0", "false", "no": + return false, nil + case "1", "true", "yes": + return true, nil + } + return false, fmt.Errorf("soap boolean: %q is not a valid boolean value", s) +} + +// MarshalBinBase64 marshals []byte to SOAP "bin.base64" type. +func MarshalBinBase64(v []byte) (string, error) { + return base64.StdEncoding.EncodeToString(v), nil +} + +// UnmarshalBinBase64 unmarshals []byte from the SOAP "bin.base64" type. +func UnmarshalBinBase64(s string) ([]byte, error) { + return base64.StdEncoding.DecodeString(s) +} + +// MarshalBinHex marshals []byte to SOAP "bin.hex" type. +func MarshalBinHex(v []byte) (string, error) { + return hex.EncodeToString(v), nil +} + +// UnmarshalBinHex unmarshals []byte from the SOAP "bin.hex" type. +func UnmarshalBinHex(s string) ([]byte, error) { + return hex.DecodeString(s) +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types_test.go b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types_test.go new file mode 100644 index 000000000..da6816190 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/soap/types_test.go @@ -0,0 +1,481 @@ +package soap + +import ( + "bytes" + "math" + "testing" + "time" +) + +type convTest interface { + Marshal() (string, error) + Unmarshal(string) (interface{}, error) + Equal(result interface{}) bool +} + +// duper is an interface that convTest values may optionally also implement to +// generate another convTest for a value in an otherwise identical testCase. +type duper interface { + Dupe(tag string) []convTest +} + +type testCase struct { + value convTest + str string + wantMarshalErr bool + wantUnmarshalErr bool + noMarshal bool + noUnMarshal bool + tag string +} + +type Ui1Test uint8 + +func (v Ui1Test) Marshal() (string, error) { + return MarshalUi1(uint8(v)) +} +func (v Ui1Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalUi1(s) +} +func (v Ui1Test) Equal(result interface{}) bool { + return uint8(v) == result.(uint8) +} +func (v Ui1Test) Dupe(tag string) []convTest { + if tag == "dupe" { + return []convTest{ + Ui2Test(v), + Ui4Test(v), + } + } + return nil +} + +type Ui2Test uint16 + +func (v Ui2Test) Marshal() (string, error) { + return MarshalUi2(uint16(v)) +} +func (v Ui2Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalUi2(s) +} +func (v Ui2Test) Equal(result interface{}) bool { + return uint16(v) == result.(uint16) +} + +type Ui4Test uint32 + +func (v Ui4Test) Marshal() (string, error) { + return MarshalUi4(uint32(v)) +} +func (v Ui4Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalUi4(s) +} +func (v Ui4Test) Equal(result interface{}) bool { + return uint32(v) == result.(uint32) +} + +type I1Test int8 + +func (v I1Test) Marshal() (string, error) { + return MarshalI1(int8(v)) +} +func (v I1Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalI1(s) +} +func (v I1Test) Equal(result interface{}) bool { + return int8(v) == result.(int8) +} +func (v I1Test) Dupe(tag string) []convTest { + if tag == "dupe" { + return []convTest{ + I2Test(v), + I4Test(v), + } + } + return nil +} + +type I2Test int16 + +func (v I2Test) Marshal() (string, error) { + return MarshalI2(int16(v)) +} +func (v I2Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalI2(s) +} +func (v I2Test) Equal(result interface{}) bool { + return int16(v) == result.(int16) +} + +type I4Test int32 + +func (v I4Test) Marshal() (string, error) { + return MarshalI4(int32(v)) +} +func (v I4Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalI4(s) +} +func (v I4Test) Equal(result interface{}) bool { + return int32(v) == result.(int32) +} + +type IntTest int64 + +func (v IntTest) Marshal() (string, error) { + return MarshalInt(int64(v)) +} +func (v IntTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalInt(s) +} +func (v IntTest) Equal(result interface{}) bool { + return int64(v) == result.(int64) +} + +type Fixed14_4Test float64 + +func (v Fixed14_4Test) Marshal() (string, error) { + return MarshalFixed14_4(float64(v)) +} +func (v Fixed14_4Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalFixed14_4(s) +} +func (v Fixed14_4Test) Equal(result interface{}) bool { + return math.Abs(float64(v)-result.(float64)) < 0.001 +} + +type CharTest rune + +func (v CharTest) Marshal() (string, error) { + return MarshalChar(rune(v)) +} +func (v CharTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalChar(s) +} +func (v CharTest) Equal(result interface{}) bool { + return rune(v) == result.(rune) +} + +type DateTest struct{ time.Time } + +func (v DateTest) Marshal() (string, error) { + return MarshalDate(time.Time(v.Time)) +} +func (v DateTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalDate(s) +} +func (v DateTest) Equal(result interface{}) bool { + return v.Time.Equal(result.(time.Time)) +} +func (v DateTest) Dupe(tag string) []convTest { + if tag != "no:dateTime" { + return []convTest{DateTimeTest{v.Time}} + } + return nil +} + +type TimeOfDayTest struct { + TimeOfDay +} + +func (v TimeOfDayTest) Marshal() (string, error) { + return MarshalTimeOfDay(v.TimeOfDay) +} +func (v TimeOfDayTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalTimeOfDay(s) +} +func (v TimeOfDayTest) Equal(result interface{}) bool { + return v.TimeOfDay == result.(TimeOfDay) +} +func (v TimeOfDayTest) Dupe(tag string) []convTest { + if tag != "no:time.tz" { + return []convTest{TimeOfDayTzTest{v.TimeOfDay}} + } + return nil +} + +type TimeOfDayTzTest struct { + TimeOfDay +} + +func (v TimeOfDayTzTest) Marshal() (string, error) { + return MarshalTimeOfDayTz(v.TimeOfDay) +} +func (v TimeOfDayTzTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalTimeOfDayTz(s) +} +func (v TimeOfDayTzTest) Equal(result interface{}) bool { + return v.TimeOfDay == result.(TimeOfDay) +} + +type DateTimeTest struct{ time.Time } + +func (v DateTimeTest) Marshal() (string, error) { + return MarshalDateTime(time.Time(v.Time)) +} +func (v DateTimeTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalDateTime(s) +} +func (v DateTimeTest) Equal(result interface{}) bool { + return v.Time.Equal(result.(time.Time)) +} +func (v DateTimeTest) Dupe(tag string) []convTest { + if tag != "no:dateTime.tz" { + return []convTest{DateTimeTzTest{v.Time}} + } + return nil +} + +type DateTimeTzTest struct{ time.Time } + +func (v DateTimeTzTest) Marshal() (string, error) { + return MarshalDateTimeTz(time.Time(v.Time)) +} +func (v DateTimeTzTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalDateTimeTz(s) +} +func (v DateTimeTzTest) Equal(result interface{}) bool { + return v.Time.Equal(result.(time.Time)) +} + +type BooleanTest bool + +func (v BooleanTest) Marshal() (string, error) { + return MarshalBoolean(bool(v)) +} +func (v BooleanTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalBoolean(s) +} +func (v BooleanTest) Equal(result interface{}) bool { + return bool(v) == result.(bool) +} + +type BinBase64Test []byte + +func (v BinBase64Test) Marshal() (string, error) { + return MarshalBinBase64([]byte(v)) +} +func (v BinBase64Test) Unmarshal(s string) (interface{}, error) { + return UnmarshalBinBase64(s) +} +func (v BinBase64Test) Equal(result interface{}) bool { + return bytes.Equal([]byte(v), result.([]byte)) +} + +type BinHexTest []byte + +func (v BinHexTest) Marshal() (string, error) { + return MarshalBinHex([]byte(v)) +} +func (v BinHexTest) Unmarshal(s string) (interface{}, error) { + return UnmarshalBinHex(s) +} +func (v BinHexTest) Equal(result interface{}) bool { + return bytes.Equal([]byte(v), result.([]byte)) +} + +func Test(t *testing.T) { + const time010203 time.Duration = (1*3600 + 2*60 + 3) * time.Second + const time0102 time.Duration = (1*3600 + 2*60) * time.Second + const time01 time.Duration = (1 * 3600) * time.Second + const time235959 time.Duration = (23*3600 + 59*60 + 59) * time.Second + + // Fake out the local time for the implementation. + localLoc = time.FixedZone("Fake/Local", 6*3600) + defer func() { + localLoc = time.Local + }() + + tests := []testCase{ + // ui1 + {str: "", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: " ", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: "abc", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: "-1", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: "0", value: Ui1Test(0), tag: "dupe"}, + {str: "1", value: Ui1Test(1), tag: "dupe"}, + {str: "255", value: Ui1Test(255), tag: "dupe"}, + {str: "256", value: Ui1Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // ui2 + {str: "65535", value: Ui2Test(65535)}, + {str: "65536", value: Ui2Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // ui4 + {str: "4294967295", value: Ui4Test(4294967295)}, + {str: "4294967296", value: Ui4Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // i1 + {str: "", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: " ", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: "abc", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true, tag: "dupe"}, + {str: "0", value: I1Test(0), tag: "dupe"}, + {str: "-1", value: I1Test(-1), tag: "dupe"}, + {str: "127", value: I1Test(127), tag: "dupe"}, + {str: "-128", value: I1Test(-128), tag: "dupe"}, + {str: "128", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true}, + {str: "-129", value: I1Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // i2 + {str: "32767", value: I2Test(32767)}, + {str: "-32768", value: I2Test(-32768)}, + {str: "32768", value: I2Test(0), wantUnmarshalErr: true, noMarshal: true}, + {str: "-32769", value: I2Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // i4 + {str: "2147483647", value: I4Test(2147483647)}, + {str: "-2147483648", value: I4Test(-2147483648)}, + {str: "2147483648", value: I4Test(0), wantUnmarshalErr: true, noMarshal: true}, + {str: "-2147483649", value: I4Test(0), wantUnmarshalErr: true, noMarshal: true}, + + // int + {str: "9223372036854775807", value: IntTest(9223372036854775807)}, + {str: "-9223372036854775808", value: IntTest(-9223372036854775808)}, + {str: "9223372036854775808", value: IntTest(0), wantUnmarshalErr: true, noMarshal: true}, + {str: "-9223372036854775809", value: IntTest(0), wantUnmarshalErr: true, noMarshal: true}, + + // fixed.14.4 + {str: "0.0000", value: Fixed14_4Test(0)}, + {str: "1.0000", value: Fixed14_4Test(1)}, + {str: "1.2346", value: Fixed14_4Test(1.23456)}, + {str: "-1.0000", value: Fixed14_4Test(-1)}, + {str: "-1.2346", value: Fixed14_4Test(-1.23456)}, + {str: "10000000000000.0000", value: Fixed14_4Test(1e13)}, + {str: "100000000000000.0000", value: Fixed14_4Test(1e14), wantMarshalErr: true, wantUnmarshalErr: true}, + {str: "-10000000000000.0000", value: Fixed14_4Test(-1e13)}, + {str: "-100000000000000.0000", value: Fixed14_4Test(-1e14), wantMarshalErr: true, wantUnmarshalErr: true}, + + // char + {str: "a", value: CharTest('a')}, + {str: "z", value: CharTest('z')}, + {str: "\u1234", value: CharTest(0x1234)}, + {str: "aa", value: CharTest(0), wantMarshalErr: true, wantUnmarshalErr: true}, + {str: "", value: CharTest(0), wantMarshalErr: true, wantUnmarshalErr: true}, + + // date + {str: "2013-10-08", value: DateTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, tag: "no:dateTime"}, + {str: "20131008", value: DateTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, noMarshal: true, tag: "no:dateTime"}, + {str: "2013-10-08T10:30:50", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime"}, + {str: "2013-10-08T10:30:50Z", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime"}, + {str: "", value: DateTest{}, wantMarshalErr: true, wantUnmarshalErr: true, noMarshal: true}, + {str: "-1", value: DateTest{}, wantUnmarshalErr: true, noMarshal: true}, + + // time + {str: "00:00:00", value: TimeOfDayTest{TimeOfDay{FromMidnight: 0}}}, + {str: "000000", value: TimeOfDayTest{TimeOfDay{FromMidnight: 0}}, noMarshal: true}, + {str: "24:00:00", value: TimeOfDayTest{TimeOfDay{FromMidnight: 24 * time.Hour}}, noMarshal: true}, // ISO8601 special case + {str: "24:01:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "24:00:01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "25:00:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "00:60:00", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "00:00:60", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "01:02:03", value: TimeOfDayTest{TimeOfDay{FromMidnight: time010203}}}, + {str: "010203", value: TimeOfDayTest{TimeOfDay{FromMidnight: time010203}}, noMarshal: true}, + {str: "23:59:59", value: TimeOfDayTest{TimeOfDay{FromMidnight: time235959}}}, + {str: "235959", value: TimeOfDayTest{TimeOfDay{FromMidnight: time235959}}, noMarshal: true}, + {str: "01:02", value: TimeOfDayTest{TimeOfDay{FromMidnight: time0102}}, noMarshal: true}, + {str: "0102", value: TimeOfDayTest{TimeOfDay{FromMidnight: time0102}}, noMarshal: true}, + {str: "01", value: TimeOfDayTest{TimeOfDay{FromMidnight: time01}}, noMarshal: true}, + {str: "foo 01:02:03", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "foo\n01:02:03", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03 foo", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03\nfoo", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03Z", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03+01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03+01:23", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03+0123", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03-01", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03-01:23", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + {str: "01:02:03-0123", value: TimeOfDayTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:time.tz"}, + + // time.tz + {str: "24:00:01", value: TimeOfDayTzTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "01Z", value: TimeOfDayTzTest{TimeOfDay{time01, true, 0}}, noMarshal: true}, + {str: "01:02:03Z", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 0}}}, + {str: "01+01", value: TimeOfDayTzTest{TimeOfDay{time01, true, 3600}}, noMarshal: true}, + {str: "01:02:03+01", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600}}, noMarshal: true}, + {str: "01:02:03+01:23", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600 + 23*60}}}, + {str: "01:02:03+0123", value: TimeOfDayTzTest{TimeOfDay{time010203, true, 3600 + 23*60}}, noMarshal: true}, + {str: "01:02:03-01", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -3600}}, noMarshal: true}, + {str: "01:02:03-01:23", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -(3600 + 23*60)}}}, + {str: "01:02:03-0123", value: TimeOfDayTzTest{TimeOfDay{time010203, true, -(3600 + 23*60)}}, noMarshal: true}, + + // dateTime + {str: "2013-10-08T00:00:00", value: DateTimeTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, tag: "no:dateTime.tz"}, + {str: "20131008", value: DateTimeTest{time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)}, noMarshal: true}, + {str: "2013-10-08T10:30:50", value: DateTimeTest{time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)}, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50T", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true}, + {str: "2013-10-08T10:30:50+01", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50+01:23", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50+0123", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50-01", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50-01:23", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + {str: "2013-10-08T10:30:50-0123", value: DateTimeTest{}, wantUnmarshalErr: true, noMarshal: true, tag: "no:dateTime.tz"}, + + // dateTime.tz + {str: "2013-10-08T10:30:50", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)}, noMarshal: true}, + {str: "2013-10-08T10:30:50+01", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:00", 3600))}, noMarshal: true}, + {str: "2013-10-08T10:30:50+01:23", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60))}}, + {str: "2013-10-08T10:30:50+0123", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60))}, noMarshal: true}, + {str: "2013-10-08T10:30:50-01", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:00", -3600))}, noMarshal: true}, + {str: "2013-10-08T10:30:50-01:23", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60)))}}, + {str: "2013-10-08T10:30:50-0123", value: DateTimeTzTest{time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60)))}, noMarshal: true}, + + // boolean + {str: "0", value: BooleanTest(false)}, + {str: "1", value: BooleanTest(true)}, + {str: "false", value: BooleanTest(false), noMarshal: true}, + {str: "true", value: BooleanTest(true), noMarshal: true}, + {str: "no", value: BooleanTest(false), noMarshal: true}, + {str: "yes", value: BooleanTest(true), noMarshal: true}, + {str: "", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true}, + {str: "other", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true}, + {str: "2", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true}, + {str: "-1", value: BooleanTest(false), noMarshal: true, wantUnmarshalErr: true}, + + // bin.base64 + {str: "", value: BinBase64Test{}}, + {str: "YQ==", value: BinBase64Test("a")}, + {str: "TG9uZ2VyIFN0cmluZy4=", value: BinBase64Test("Longer String.")}, + {str: "TG9uZ2VyIEFsaWduZWQu", value: BinBase64Test("Longer Aligned.")}, + + // bin.hex + {str: "", value: BinHexTest{}}, + {str: "61", value: BinHexTest("a")}, + {str: "4c6f6e67657220537472696e672e", value: BinHexTest("Longer String.")}, + {str: "4C6F6E67657220537472696E672E", value: BinHexTest("Longer String."), noMarshal: true}, + } + + // Generate extra test cases from convTests that implement duper. + var extras []testCase + for i := range tests { + if duper, ok := tests[i].value.(duper); ok { + dupes := duper.Dupe(tests[i].tag) + for _, duped := range dupes { + dupedCase := testCase(tests[i]) + dupedCase.value = duped + extras = append(extras, dupedCase) + } + } + } + tests = append(tests, extras...) + + for _, test := range tests { + if test.noMarshal { + } else if resultStr, err := test.value.Marshal(); err != nil && !test.wantMarshalErr { + t.Errorf("For %T marshal %v, want %q, got error: %v", test.value, test.value, test.str, err) + } else if err == nil && test.wantMarshalErr { + t.Errorf("For %T marshal %v, want error, got %q", test.value, test.value, resultStr) + } else if err == nil && resultStr != test.str { + t.Errorf("For %T marshal %v, want %q, got %q", test.value, test.value, test.str, resultStr) + } + + if test.noUnMarshal { + } else if resultValue, err := test.value.Unmarshal(test.str); err != nil && !test.wantUnmarshalErr { + t.Errorf("For %T unmarshal %q, want %v, got error: %v", test.value, test.str, test.value, err) + } else if err == nil && test.wantUnmarshalErr { + t.Errorf("For %T unmarshal %q, want error, got %v", test.value, test.str, resultValue) + } else if err == nil && !test.value.Equal(resultValue) { + t.Errorf("For %T unmarshal %q, want %v, got %v", test.value, test.str, test.value, resultValue) + } + } +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/registry.go b/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/registry.go new file mode 100644 index 000000000..9e2611b7f --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/registry.go @@ -0,0 +1,202 @@ +package ssdp + +import ( + "fmt" + "log" + "net/http" + "net/url" + "regexp" + "strconv" + "sync" + "time" + + "github.com/fjl/goupnp/httpu" +) + +const ( + maxExpiryTimeSeconds = 24 * 60 * 60 +) + +var ( + maxAgeRx = regexp.MustCompile("max-age=([0-9]+)") +) + +type Entry struct { + // The address that the entry data was actually received from. + RemoteAddr string + // Unique Service Name. Identifies a unique instance of a device or service. + USN string + // Notfication Type. The type of device or service being announced. + NT string + // Server's self-identifying string. + Server string + Host string + // Location of the UPnP root device description. + Location *url.URL + + // Despite BOOTID,CONFIGID being required fields, apparently they are not + // always set by devices. Set to -1 if not present. + + BootID int32 + ConfigID int32 + + SearchPort uint16 + + // When the last update was received for this entry identified by this USN. + LastUpdate time.Time + // When the last update's cached values are advised to expire. + CacheExpiry time.Time +} + +func newEntryFromRequest(r *http.Request) (*Entry, error) { + now := time.Now() + expiryDuration, err := parseCacheControlMaxAge(r.Header.Get("CACHE-CONTROL")) + if err != nil { + return nil, fmt.Errorf("ssdp: error parsing CACHE-CONTROL max age: %v", err) + } + + loc, err := url.Parse(r.Header.Get("LOCATION")) + if err != nil { + return nil, fmt.Errorf("ssdp: error parsing entry Location URL: %v", err) + } + + bootID, err := parseUpnpIntHeader(r.Header, "BOOTID.UPNP.ORG", -1) + if err != nil { + return nil, err + } + configID, err := parseUpnpIntHeader(r.Header, "CONFIGID.UPNP.ORG", -1) + if err != nil { + return nil, err + } + searchPort, err := parseUpnpIntHeader(r.Header, "SEARCHPORT.UPNP.ORG", ssdpSearchPort) + if err != nil { + return nil, err + } + + if searchPort < 1 || searchPort > 65535 { + return nil, fmt.Errorf("ssdp: search port %d is out of range", searchPort) + } + + return &Entry{ + RemoteAddr: r.RemoteAddr, + USN: r.Header.Get("USN"), + NT: r.Header.Get("NT"), + Server: r.Header.Get("SERVER"), + Host: r.Header.Get("HOST"), + Location: loc, + BootID: bootID, + ConfigID: configID, + SearchPort: uint16(searchPort), + LastUpdate: now, + CacheExpiry: now.Add(expiryDuration), + }, nil +} + +func parseCacheControlMaxAge(cc string) (time.Duration, error) { + matches := maxAgeRx.FindStringSubmatch(cc) + if len(matches) != 2 { + return 0, fmt.Errorf("did not find exactly one max-age in cache control header: %q", cc) + } + expirySeconds, err := strconv.ParseInt(matches[1], 10, 16) + if err != nil { + return 0, err + } + if expirySeconds < 1 || expirySeconds > maxExpiryTimeSeconds { + return 0, fmt.Errorf("rejecting bad expiry time of %d seconds", expirySeconds) + } + return time.Duration(expirySeconds) * time.Second, nil +} + +// parseUpnpIntHeader is intended to parse the +// {BOOT,CONFIGID,SEARCHPORT}.UPNP.ORG header fields. It returns the def if +// the head is empty or missing. +func parseUpnpIntHeader(headers http.Header, headerName string, def int32) (int32, error) { + s := headers.Get(headerName) + if s == "" { + return def, nil + } + v, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return 0, fmt.Errorf("ssdp: could not parse header %s: %v", headerName, err) + } + return int32(v), nil +} + +var _ httpu.Handler = new(Registry) + +// Registry maintains knowledge of discovered devices and services. +type Registry struct { + lock sync.Mutex + byUSN map[string]*Entry +} + +func NewRegistry() *Registry { + return &Registry{ + byUSN: make(map[string]*Entry), + } +} + +// ServeMessage implements httpu.Handler, and uses SSDP NOTIFY requests to +// maintain the registry of devices and services. +func (reg *Registry) ServeMessage(r *http.Request) { + if r.Method != methodNotify { + return + } + + nts := r.Header.Get("nts") + + var err error + switch nts { + case ntsAlive: + err = reg.handleNTSAlive(r) + case ntsUpdate: + err = reg.handleNTSUpdate(r) + case ntsByebye: + err = reg.handleNTSByebye(r) + default: + err = fmt.Errorf("unknown NTS value: %q", nts) + } + log.Printf("In %s request from %s: %v", nts, r.RemoteAddr, err) +} + +func (reg *Registry) handleNTSAlive(r *http.Request) error { + entry, err := newEntryFromRequest(r) + if err != nil { + return err + } + + reg.lock.Lock() + defer reg.lock.Unlock() + + reg.byUSN[entry.USN] = entry + + return nil +} + +func (reg *Registry) handleNTSUpdate(r *http.Request) error { + entry, err := newEntryFromRequest(r) + if err != nil { + return err + } + nextBootID, err := parseUpnpIntHeader(r.Header, "NEXTBOOTID.UPNP.ORG", -1) + if err != nil { + return err + } + entry.BootID = nextBootID + + reg.lock.Lock() + defer reg.lock.Unlock() + + reg.byUSN[entry.USN] = entry + + return nil +} + +func (reg *Registry) handleNTSByebye(r *http.Request) error { + reg.lock.Lock() + defer reg.lock.Unlock() + + delete(reg.byUSN, r.Header.Get("USN")) + + return nil +} diff --git a/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/ssdp.go b/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/ssdp.go new file mode 100644 index 000000000..6a186afa5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fjl/goupnp/ssdp/ssdp.go @@ -0,0 +1,83 @@ +package ssdp + +import ( + "errors" + "log" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/fjl/goupnp/httpu" +) + +const ( + ssdpDiscover = `"ssdp:discover"` + ntsAlive = `ssdp:alive` + ntsByebye = `ssdp:byebye` + ntsUpdate = `ssdp:update` + ssdpUDP4Addr = "239.255.255.250:1900" + ssdpSearchPort = 1900 + methodSearch = "M-SEARCH" + methodNotify = "NOTIFY" +) + +// SSDPRawSearch performs a fairly raw SSDP search request, and returns the +// unique response(s) that it receives. Each response has the requested +// searchTarget, a USN, and a valid location. maxWaitSeconds states how long to +// wait for responses in seconds, and must be a minimum of 1 (the +// implementation waits an additional 100ms for responses to arrive), 2 is a +// reasonable value for this. numSends is the number of requests to send - 3 is +// a reasonable value for this. +func SSDPRawSearch(httpu *httpu.HTTPUClient, searchTarget string, maxWaitSeconds int, numSends int) ([]*http.Response, error) { + if maxWaitSeconds < 1 { + return nil, errors.New("ssdp: maxWaitSeconds must be >= 1") + } + + seenUsns := make(map[string]bool) + var responses []*http.Response + req := http.Request{ + Method: methodSearch, + // TODO: Support both IPv4 and IPv6. + Host: ssdpUDP4Addr, + URL: &url.URL{Opaque: "*"}, + Header: http.Header{ + // Putting headers in here avoids them being title-cased. + // (The UPnP discovery protocol uses case-sensitive headers) + "HOST": []string{ssdpUDP4Addr}, + "MX": []string{strconv.FormatInt(int64(maxWaitSeconds), 10)}, + "MAN": []string{ssdpDiscover}, + "ST": []string{searchTarget}, + }, + } + allResponses, err := httpu.Do(&req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends) + if err != nil { + return nil, err + } + for _, response := range allResponses { + if response.StatusCode != 200 { + log.Printf("ssdp: got response status code %q in search response", response.Status) + continue + } + if st := response.Header.Get("ST"); st != searchTarget { + log.Printf("ssdp: got unexpected search target result %q", st) + continue + } + location, err := response.Location() + if err != nil { + log.Printf("ssdp: no usable location in search response (discarding): %v", err) + continue + } + usn := response.Header.Get("USN") + if usn == "" { + log.Printf("ssdp: empty/missing USN in search response (using location instead): %v", err) + usn = location.String() + } + if _, alreadySeen := seenUsns[usn]; !alreadySeen { + seenUsns[usn] = true + responses = append(responses, response) + } + } + + return responses, nil +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore b/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore new file mode 100644 index 000000000..e4706a9e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore @@ -0,0 +1,5 @@ +# Setup a Global .gitignore for OS and editor generated files: +# https://help.github.com/articles/ignoring-files +# git config --global core.excludesfile ~/.gitignore_global + +.vagrant diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS b/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS new file mode 100644 index 000000000..e52b72f83 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS @@ -0,0 +1,28 @@ +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# You can update this list using the following command: +# +# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' + +# Please keep the list sorted. + +Adrien Bustany +Caleb Spare +Case Nelson +Chris Howey +Christoffer Buchholz +Dave Cheney +Francisco Souza +John C Barstow +Kelvin Fo +Nathan Youngman +Paul Hammond +Pursuit92 +Rob Figueiredo +Travis Cline +Tudor Golubenco +bronze1man +debrando +henrikedwards diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md new file mode 100644 index 000000000..761686aa9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md @@ -0,0 +1,160 @@ +# Changelog + +## v0.9.0 / 2014-01-17 + +* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) +* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) +* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. + +## v0.8.12 / 2013-11-13 + +* [API] Remove FD_SET and friends from Linux adapter + +## v0.8.11 / 2013-11-02 + +* [Doc] Add Changelog [#72][] (thanks @nathany) +* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond) + +## v0.8.10 / 2013-10-19 + +* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) +* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) +* [Doc] specify OS-specific limits in README (thanks @debrando) + +## v0.8.9 / 2013-09-08 + +* [Doc] Contributing (thanks @nathany) +* [Doc] update package path in example code [#63][] (thanks @paulhammond) +* [Doc] GoCI badge in README (Linux only) [#60][] +* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) + +## v0.8.8 / 2013-06-17 + +* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) + +## v0.8.7 / 2013-06-03 + +* [API] Make syscall flags internal +* [Fix] inotify: ignore event changes +* [Fix] race in symlink test [#45][] (reported by @srid) +* [Fix] tests on Windows +* lower case error messages + +## v0.8.6 / 2013-05-23 + +* kqueue: Use EVT_ONLY flag on Darwin +* [Doc] Update README with full example + +## v0.8.5 / 2013-05-09 + +* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) + +## v0.8.4 / 2013-04-07 + +* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) + +## v0.8.3 / 2013-03-13 + +* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) +* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) + +## v0.8.2 / 2013-02-07 + +* [Doc] add Authors +* [Fix] fix data races for map access [#29][] (thanks @fsouza) + +## v0.8.1 / 2013-01-09 + +* [Fix] Windows path separators +* [Doc] BSD License + +## v0.8.0 / 2012-11-09 + +* kqueue: directory watching improvements (thanks @vmirage) +* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) +* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) + +## v0.7.4 / 2012-10-09 + +* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) +* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) +* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) +* [Fix] kqueue: modify after recreation of file + +## v0.7.3 / 2012-09-27 + +* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) +* [Fix] kqueue: no longer get duplicate CREATE events + +## v0.7.2 / 2012-09-01 + +* kqueue: events for created directories + +## v0.7.1 / 2012-07-14 + +* [Fix] for renaming files + +## v0.7.0 / 2012-07-02 + +* [Feature] FSNotify flags +* [Fix] inotify: Added file name back to event path + +## v0.6.0 / 2012-06-06 + +* kqueue: watch files after directory created (thanks @tmc) + +## v0.5.1 / 2012-05-22 + +* [Fix] inotify: remove all watches before Close() + +## v0.5.0 / 2012-05-03 + +* [API] kqueue: return errors during watch instead of sending over channel +* kqueue: match symlink behavior on Linux +* inotify: add `DELETE_SELF` (requested by @taralx) +* [Fix] kqueue: handle EINTR (reported by @robfig) +* [Doc] Godoc example [#1][] (thanks @davecheney) + +## v0.4.0 / 2012-03-30 + +* Go 1 released: build with go tool +* [Feature] Windows support using winfsnotify +* Windows does not have attribute change notifications +* Roll attribute notifications into IsModify + +## v0.3.0 / 2012-02-19 + +* kqueue: add files when watch directory + +## v0.2.0 / 2011-12-30 + +* update to latest Go weekly code + +## v0.1.0 / 2011-10-19 + +* kqueue: add watch on file creation to match inotify +* kqueue: create file event +* inotify: ignore `IN_IGNORED` events +* event String() +* linux: common FileEvent functions +* initial commit + +[#79]: https://github.com/howeyc/fsnotify/pull/79 +[#77]: https://github.com/howeyc/fsnotify/pull/77 +[#72]: https://github.com/howeyc/fsnotify/issues/72 +[#71]: https://github.com/howeyc/fsnotify/issues/71 +[#70]: https://github.com/howeyc/fsnotify/issues/70 +[#63]: https://github.com/howeyc/fsnotify/issues/63 +[#62]: https://github.com/howeyc/fsnotify/issues/62 +[#60]: https://github.com/howeyc/fsnotify/issues/60 +[#59]: https://github.com/howeyc/fsnotify/issues/59 +[#49]: https://github.com/howeyc/fsnotify/issues/49 +[#45]: https://github.com/howeyc/fsnotify/issues/45 +[#40]: https://github.com/howeyc/fsnotify/issues/40 +[#36]: https://github.com/howeyc/fsnotify/issues/36 +[#33]: https://github.com/howeyc/fsnotify/issues/33 +[#29]: https://github.com/howeyc/fsnotify/issues/29 +[#25]: https://github.com/howeyc/fsnotify/issues/25 +[#24]: https://github.com/howeyc/fsnotify/issues/24 +[#21]: https://github.com/howeyc/fsnotify/issues/21 +[#1]: https://github.com/howeyc/fsnotify/issues/1 diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md new file mode 100644 index 000000000..b2025d72c --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing + +## Moving Notice + +There is a fork being actively developed with a new API in preparation for the Go Standard Library: +[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify) + diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE b/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE new file mode 100644 index 000000000..f21e54080 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2012 fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md new file mode 100644 index 000000000..4c7498d38 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md @@ -0,0 +1,92 @@ +# File system notifications for Go + +[![GoDoc](https://godoc.org/github.com/howeyc/fsnotify?status.png)](http://godoc.org/github.com/howeyc/fsnotify) + +Cross platform: Windows, Linux, BSD and OS X. + +## Moving Notice + +There is a fork being actively developed with a new API in preparation for the Go Standard Library: +[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify) + +## Example: + +```go +package main + +import ( + "log" + + "github.com/howeyc/fsnotify" +) + +func main() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + + done := make(chan bool) + + // Process events + go func() { + for { + select { + case ev := <-watcher.Event: + log.Println("event:", ev) + case err := <-watcher.Error: + log.Println("error:", err) + } + } + }() + + err = watcher.Watch("testDir") + if err != nil { + log.Fatal(err) + } + + <-done + + /* ... do stuff ... */ + watcher.Close() +} +``` + +For each event: +* Name +* IsCreate() +* IsDelete() +* IsModify() +* IsRename() + +## FAQ + +**When a file is moved to another directory is it still being watched?** + +No (it shouldn't be, unless you are watching where it was moved to). + +**When I watch a directory, are all subdirectories watched as well?** + +No, you must add watches for any directory you want to watch (a recursive watcher is in the works [#56][]). + +**Do I have to watch the Error and Event channels in a separate goroutine?** + +As of now, yes. Looking into making this single-thread friendly (see [#7][]) + +**Why am I receiving multiple events for the same file on OS X?** + +Spotlight indexing on OS X can result in multiple events (see [#62][]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#54][]). + +**How many files can be watched at once?** + +There are OS-specific limits as to how many watches can be created: +* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, +reaching this limit results in a "no space left on device" error. +* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. + + +[#62]: https://github.com/howeyc/fsnotify/issues/62 +[#56]: https://github.com/howeyc/fsnotify/issues/56 +[#54]: https://github.com/howeyc/fsnotify/issues/54 +[#7]: https://github.com/howeyc/fsnotify/issues/7 + diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go new file mode 100644 index 000000000..d3130e222 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go @@ -0,0 +1,34 @@ +// 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 fsnotify_test + +import ( + "log" + + "github.com/howeyc/fsnotify" +) + +func ExampleNewWatcher() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + + go func() { + for { + select { + case ev := <-watcher.Event: + log.Println("event:", ev) + case err := <-watcher.Error: + log.Println("error:", err) + } + } + }() + + err = watcher.Watch("/tmp/foo") + if err != nil { + log.Fatal(err) + } +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go new file mode 100644 index 000000000..9a48d847d --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go @@ -0,0 +1,111 @@ +// 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 fsnotify implements file system notification. +package fsnotify + +import "fmt" + +const ( + FSN_CREATE = 1 + FSN_MODIFY = 2 + FSN_DELETE = 4 + FSN_RENAME = 8 + + FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE +) + +// Purge events from interal chan to external chan if passes filter +func (w *Watcher) purgeEvents() { + for ev := range w.internalEvent { + sendEvent := false + w.fsnmut.Lock() + fsnFlags := w.fsnFlags[ev.Name] + w.fsnmut.Unlock() + + if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() { + sendEvent = true + } + + if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() { + sendEvent = true + } + + if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() { + sendEvent = true + } + + if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() { + sendEvent = true + } + + if sendEvent { + w.Event <- ev + } + + // If there's no file, then no more events for user + // BSD must keep watch for internal use (watches DELETEs to keep track + // what files exist for create events) + if ev.IsDelete() { + w.fsnmut.Lock() + delete(w.fsnFlags, ev.Name) + w.fsnmut.Unlock() + } + } + + close(w.Event) +} + +// Watch a given file path +func (w *Watcher) Watch(path string) error { + return w.WatchFlags(path, FSN_ALL) +} + +// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.) +func (w *Watcher) WatchFlags(path string, flags uint32) error { + w.fsnmut.Lock() + w.fsnFlags[path] = flags + w.fsnmut.Unlock() + return w.watch(path) +} + +// Remove a watch on a file +func (w *Watcher) RemoveWatch(path string) error { + w.fsnmut.Lock() + delete(w.fsnFlags, path) + w.fsnmut.Unlock() + return w.removeWatch(path) +} + +// String formats the event e in the form +// "filename: DELETE|MODIFY|..." +func (e *FileEvent) String() string { + var events string = "" + + if e.IsCreate() { + events += "|" + "CREATE" + } + + if e.IsDelete() { + events += "|" + "DELETE" + } + + if e.IsModify() { + events += "|" + "MODIFY" + } + + if e.IsRename() { + events += "|" + "RENAME" + } + + if e.IsAttrib() { + events += "|" + "ATTRIB" + } + + if len(events) > 0 { + events = events[1:] + } + + return fmt.Sprintf("%q: %s", e.Name, events) +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go new file mode 100644 index 000000000..e6ffd7e5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go @@ -0,0 +1,496 @@ +// 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 freebsd openbsd netbsd darwin + +package fsnotify + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + "syscall" +) + +const ( + // Flags (from ) + sys_NOTE_DELETE = 0x0001 /* vnode was removed */ + sys_NOTE_WRITE = 0x0002 /* data contents changed */ + sys_NOTE_EXTEND = 0x0004 /* size increased */ + sys_NOTE_ATTRIB = 0x0008 /* attributes changed */ + sys_NOTE_LINK = 0x0010 /* link count changed */ + sys_NOTE_RENAME = 0x0020 /* vnode was renamed */ + sys_NOTE_REVOKE = 0x0040 /* vnode access was revoked */ + + // Watch all events + sys_NOTE_ALLEVENTS = sys_NOTE_DELETE | sys_NOTE_WRITE | sys_NOTE_ATTRIB | sys_NOTE_RENAME + + // Block for 100 ms on each call to kevent + keventWaitTime = 100e6 +) + +type FileEvent struct { + mask uint32 // Mask of events + Name string // File name (optional) + create bool // set by fsnotify package if found new file +} + +// IsCreate reports whether the FileEvent was triggered by a creation +func (e *FileEvent) IsCreate() bool { return e.create } + +// IsDelete reports whether the FileEvent was triggered by a delete +func (e *FileEvent) IsDelete() bool { return (e.mask & sys_NOTE_DELETE) == sys_NOTE_DELETE } + +// IsModify reports whether the FileEvent was triggered by a file modification +func (e *FileEvent) IsModify() bool { + return ((e.mask&sys_NOTE_WRITE) == sys_NOTE_WRITE || (e.mask&sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB) +} + +// IsRename reports whether the FileEvent was triggered by a change name +func (e *FileEvent) IsRename() bool { return (e.mask & sys_NOTE_RENAME) == sys_NOTE_RENAME } + +// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. +func (e *FileEvent) IsAttrib() bool { + return (e.mask & sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB +} + +type Watcher struct { + mu sync.Mutex // Mutex for the Watcher itself. + kq int // File descriptor (as returned by the kqueue() syscall) + watches map[string]int // Map of watched file descriptors (key: path) + wmut sync.Mutex // Protects access to watches. + fsnFlags map[string]uint32 // Map of watched files to flags used for filter + fsnmut sync.Mutex // Protects access to fsnFlags. + enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue + enmut sync.Mutex // Protects access to enFlags. + paths map[int]string // Map of watched paths (key: watch descriptor) + finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor) + pmut sync.Mutex // Protects access to paths and finfo. + fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events) + femut sync.Mutex // Protects access to fileExists. + externalWatches map[string]bool // Map of watches added by user of the library. + ewmut sync.Mutex // Protects access to externalWatches. + Error chan error // Errors are sent on this channel + internalEvent chan *FileEvent // Events are queued on this channel + Event chan *FileEvent // Events are returned on this channel + done chan bool // Channel for sending a "quit message" to the reader goroutine + isClosed bool // Set to true when Close() is first called +} + +// NewWatcher creates and returns a new kevent instance using kqueue(2) +func NewWatcher() (*Watcher, error) { + fd, errno := syscall.Kqueue() + if fd == -1 { + return nil, os.NewSyscallError("kqueue", errno) + } + w := &Watcher{ + kq: fd, + watches: make(map[string]int), + fsnFlags: make(map[string]uint32), + enFlags: make(map[string]uint32), + paths: make(map[int]string), + finfo: make(map[int]os.FileInfo), + fileExists: make(map[string]bool), + externalWatches: make(map[string]bool), + internalEvent: make(chan *FileEvent), + Event: make(chan *FileEvent), + Error: make(chan error), + done: make(chan bool, 1), + } + + go w.readEvents() + go w.purgeEvents() + return w, nil +} + +// Close closes a kevent watcher instance +// It sends a message to the reader goroutine to quit and removes all watches +// associated with the kevent instance +func (w *Watcher) Close() error { + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return nil + } + w.isClosed = true + w.mu.Unlock() + + // Send "quit" message to the reader goroutine + w.done <- true + w.wmut.Lock() + ws := w.watches + w.wmut.Unlock() + for path := range ws { + w.removeWatch(path) + } + + return nil +} + +// AddWatch adds path to the watched file set. +// The flags are interpreted as described in kevent(2). +func (w *Watcher) addWatch(path string, flags uint32) error { + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return errors.New("kevent instance already closed") + } + w.mu.Unlock() + + watchDir := false + + w.wmut.Lock() + watchfd, found := w.watches[path] + w.wmut.Unlock() + if !found { + fi, errstat := os.Lstat(path) + if errstat != nil { + return errstat + } + + // don't watch socket + if fi.Mode()&os.ModeSocket == os.ModeSocket { + return nil + } + + // Follow Symlinks + // Unfortunately, Linux can add bogus symlinks to watch list without + // issue, and Windows can't do symlinks period (AFAIK). To maintain + // consistency, we will act like everything is fine. There will simply + // be no file events for broken symlinks. + // Hence the returns of nil on errors. + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + path, err := filepath.EvalSymlinks(path) + if err != nil { + return nil + } + + fi, errstat = os.Lstat(path) + if errstat != nil { + return nil + } + } + + fd, errno := syscall.Open(path, open_FLAGS, 0700) + if fd == -1 { + return errno + } + watchfd = fd + + w.wmut.Lock() + w.watches[path] = watchfd + w.wmut.Unlock() + + w.pmut.Lock() + w.paths[watchfd] = path + w.finfo[watchfd] = fi + w.pmut.Unlock() + } + // Watch the directory if it has not been watched before. + w.pmut.Lock() + w.enmut.Lock() + if w.finfo[watchfd].IsDir() && + (flags&sys_NOTE_WRITE) == sys_NOTE_WRITE && + (!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) { + watchDir = true + } + w.enmut.Unlock() + w.pmut.Unlock() + + w.enmut.Lock() + w.enFlags[path] = flags + w.enmut.Unlock() + + var kbuf [1]syscall.Kevent_t + watchEntry := &kbuf[0] + watchEntry.Fflags = flags + syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) + entryFlags := watchEntry.Flags + success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) + if success == -1 { + return errno + } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { + return errors.New("kevent add error") + } + + if watchDir { + errdir := w.watchDirectoryFiles(path) + if errdir != nil { + return errdir + } + } + return nil +} + +// Watch adds path to the watched file set, watching all events. +func (w *Watcher) watch(path string) error { + w.ewmut.Lock() + w.externalWatches[path] = true + w.ewmut.Unlock() + return w.addWatch(path, sys_NOTE_ALLEVENTS) +} + +// RemoveWatch removes path from the watched file set. +func (w *Watcher) removeWatch(path string) error { + w.wmut.Lock() + watchfd, ok := w.watches[path] + w.wmut.Unlock() + if !ok { + return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) + } + var kbuf [1]syscall.Kevent_t + watchEntry := &kbuf[0] + syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) + entryFlags := watchEntry.Flags + success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) + if success == -1 { + return os.NewSyscallError("kevent_rm_watch", errno) + } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { + return errors.New("kevent rm error") + } + syscall.Close(watchfd) + w.wmut.Lock() + delete(w.watches, path) + w.wmut.Unlock() + w.enmut.Lock() + delete(w.enFlags, path) + w.enmut.Unlock() + w.pmut.Lock() + delete(w.paths, watchfd) + fInfo := w.finfo[watchfd] + delete(w.finfo, watchfd) + w.pmut.Unlock() + + // Find all watched paths that are in this directory that are not external. + if fInfo.IsDir() { + var pathsToRemove []string + w.pmut.Lock() + for _, wpath := range w.paths { + wdir, _ := filepath.Split(wpath) + if filepath.Clean(wdir) == filepath.Clean(path) { + w.ewmut.Lock() + if !w.externalWatches[wpath] { + pathsToRemove = append(pathsToRemove, wpath) + } + w.ewmut.Unlock() + } + } + w.pmut.Unlock() + for _, p := range pathsToRemove { + // Since these are internal, not much sense in propagating error + // to the user, as that will just confuse them with an error about + // a path they did not explicitly watch themselves. + w.removeWatch(p) + } + } + + return nil +} + +// readEvents reads from the kqueue file descriptor, converts the +// received events into Event objects and sends them via the Event channel +func (w *Watcher) readEvents() { + var ( + eventbuf [10]syscall.Kevent_t // Event buffer + events []syscall.Kevent_t // Received events + twait *syscall.Timespec // Time to block waiting for events + n int // Number of events returned from kevent + errno error // Syscall errno + ) + events = eventbuf[0:0] + twait = new(syscall.Timespec) + *twait = syscall.NsecToTimespec(keventWaitTime) + + for { + // See if there is a message on the "done" channel + var done bool + select { + case done = <-w.done: + default: + } + + // If "done" message is received + if done { + errno := syscall.Close(w.kq) + if errno != nil { + w.Error <- os.NewSyscallError("close", errno) + } + close(w.internalEvent) + close(w.Error) + return + } + + // Get new events + if len(events) == 0 { + n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait) + + // EINTR is okay, basically the syscall was interrupted before + // timeout expired. + if errno != nil && errno != syscall.EINTR { + w.Error <- os.NewSyscallError("kevent", errno) + continue + } + + // Received some events + if n > 0 { + events = eventbuf[0:n] + } + } + + // Flush the events we received to the events channel + for len(events) > 0 { + fileEvent := new(FileEvent) + watchEvent := &events[0] + fileEvent.mask = uint32(watchEvent.Fflags) + w.pmut.Lock() + fileEvent.Name = w.paths[int(watchEvent.Ident)] + fileInfo := w.finfo[int(watchEvent.Ident)] + w.pmut.Unlock() + if fileInfo != nil && fileInfo.IsDir() && !fileEvent.IsDelete() { + // Double check to make sure the directory exist. This can happen when + // we do a rm -fr on a recursively watched folders and we receive a + // modification event first but the folder has been deleted and later + // receive the delete event + if _, err := os.Lstat(fileEvent.Name); os.IsNotExist(err) { + // mark is as delete event + fileEvent.mask |= sys_NOTE_DELETE + } + } + + if fileInfo != nil && fileInfo.IsDir() && fileEvent.IsModify() && !fileEvent.IsDelete() { + w.sendDirectoryChangeEvents(fileEvent.Name) + } else { + // Send the event on the events channel + w.internalEvent <- fileEvent + } + + // Move to next event + events = events[1:] + + if fileEvent.IsRename() { + w.removeWatch(fileEvent.Name) + w.femut.Lock() + delete(w.fileExists, fileEvent.Name) + w.femut.Unlock() + } + if fileEvent.IsDelete() { + w.removeWatch(fileEvent.Name) + w.femut.Lock() + delete(w.fileExists, fileEvent.Name) + w.femut.Unlock() + + // Look for a file that may have overwritten this + // (ie mv f1 f2 will delete f2 then create f2) + fileDir, _ := filepath.Split(fileEvent.Name) + fileDir = filepath.Clean(fileDir) + w.wmut.Lock() + _, found := w.watches[fileDir] + w.wmut.Unlock() + if found { + // make sure the directory exist before we watch for changes. When we + // do a recursive watch and perform rm -fr, the parent directory might + // have gone missing, ignore the missing directory and let the + // upcoming delete event remove the watch form the parent folder + if _, err := os.Lstat(fileDir); !os.IsNotExist(err) { + w.sendDirectoryChangeEvents(fileDir) + } + } + } + } + } +} + +func (w *Watcher) watchDirectoryFiles(dirPath string) error { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + return err + } + + // Search for new files + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + + // Inherit fsnFlags from parent directory + w.fsnmut.Lock() + if flags, found := w.fsnFlags[dirPath]; found { + w.fsnFlags[filePath] = flags + } else { + w.fsnFlags[filePath] = FSN_ALL + } + w.fsnmut.Unlock() + + if fileInfo.IsDir() == false { + // Watch file to mimic linux fsnotify + e := w.addWatch(filePath, sys_NOTE_ALLEVENTS) + if e != nil { + return e + } + } else { + // If the user is currently watching directory + // we want to preserve the flags used + w.enmut.Lock() + currFlags, found := w.enFlags[filePath] + w.enmut.Unlock() + var newFlags uint32 = sys_NOTE_DELETE + if found { + newFlags |= currFlags + } + + // Linux gives deletes if not explicitly watching + e := w.addWatch(filePath, newFlags) + if e != nil { + return e + } + } + w.femut.Lock() + w.fileExists[filePath] = true + w.femut.Unlock() + } + + return nil +} + +// sendDirectoryEvents searches the directory for newly created files +// and sends them over the event channel. This functionality is to have +// the BSD version of fsnotify match linux fsnotify which provides a +// create event for files created in a watched directory. +func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + w.Error <- err + } + + // Search for new files + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + w.femut.Lock() + _, doesExist := w.fileExists[filePath] + w.femut.Unlock() + if !doesExist { + // Inherit fsnFlags from parent directory + w.fsnmut.Lock() + if flags, found := w.fsnFlags[dirPath]; found { + w.fsnFlags[filePath] = flags + } else { + w.fsnFlags[filePath] = FSN_ALL + } + w.fsnmut.Unlock() + + // Send create event + fileEvent := new(FileEvent) + fileEvent.Name = filePath + fileEvent.create = true + w.internalEvent <- fileEvent + } + w.femut.Lock() + w.fileExists[filePath] = true + w.femut.Unlock() + } + w.watchDirectoryFiles(dirPath) +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go new file mode 100644 index 000000000..80ade879f --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go @@ -0,0 +1,304 @@ +// 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 linux + +package fsnotify + +import ( + "errors" + "fmt" + "os" + "strings" + "sync" + "syscall" + "unsafe" +) + +const ( + // Options for inotify_init() are not exported + // sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC + // sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK + + // Options for AddWatch + sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW + sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT + sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR + + // The "sys_IN_MASK_ADD" option is not exported, as AddWatch + // adds it automatically, if there is already a watch for the given path + // sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD + + // Events + sys_IN_ACCESS uint32 = syscall.IN_ACCESS + sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS + sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB + sys_IN_CLOSE uint32 = syscall.IN_CLOSE + sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE + sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE + sys_IN_CREATE uint32 = syscall.IN_CREATE + sys_IN_DELETE uint32 = syscall.IN_DELETE + sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF + sys_IN_MODIFY uint32 = syscall.IN_MODIFY + sys_IN_MOVE uint32 = syscall.IN_MOVE + sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM + sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO + sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF + sys_IN_OPEN uint32 = syscall.IN_OPEN + + sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF + + // Special events + sys_IN_ISDIR uint32 = syscall.IN_ISDIR + sys_IN_IGNORED uint32 = syscall.IN_IGNORED + sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW + sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT +) + +type FileEvent struct { + mask uint32 // Mask of events + cookie uint32 // Unique cookie associating related events (for rename(2)) + Name string // File name (optional) +} + +// IsCreate reports whether the FileEvent was triggered by a creation +func (e *FileEvent) IsCreate() bool { + return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO +} + +// IsDelete reports whether the FileEvent was triggered by a delete +func (e *FileEvent) IsDelete() bool { + return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE +} + +// IsModify reports whether the FileEvent was triggered by a file modification or attribute change +func (e *FileEvent) IsModify() bool { + return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB) +} + +// IsRename reports whether the FileEvent was triggered by a change name +func (e *FileEvent) IsRename() bool { + return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM) +} + +// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. +func (e *FileEvent) IsAttrib() bool { + return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB +} + +type watch struct { + wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) + flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) +} + +type Watcher struct { + mu sync.Mutex // Map access + fd int // File descriptor (as returned by the inotify_init() syscall) + watches map[string]*watch // Map of inotify watches (key: path) + fsnFlags map[string]uint32 // Map of watched files to flags used for filter + fsnmut sync.Mutex // Protects access to fsnFlags. + paths map[int]string // Map of watched paths (key: watch descriptor) + Error chan error // Errors are sent on this channel + internalEvent chan *FileEvent // Events are queued on this channel + Event chan *FileEvent // Events are returned on this channel + done chan bool // Channel for sending a "quit message" to the reader goroutine + isClosed bool // Set to true when Close() is first called +} + +// NewWatcher creates and returns a new inotify instance using inotify_init(2) +func NewWatcher() (*Watcher, error) { + fd, errno := syscall.InotifyInit() + if fd == -1 { + return nil, os.NewSyscallError("inotify_init", errno) + } + w := &Watcher{ + fd: fd, + watches: make(map[string]*watch), + fsnFlags: make(map[string]uint32), + paths: make(map[int]string), + internalEvent: make(chan *FileEvent), + Event: make(chan *FileEvent), + Error: make(chan error), + done: make(chan bool, 1), + } + + go w.readEvents() + go w.purgeEvents() + return w, nil +} + +// Close closes an inotify watcher instance +// It sends a message to the reader goroutine to quit and removes all watches +// associated with the inotify instance +func (w *Watcher) Close() error { + if w.isClosed { + return nil + } + w.isClosed = true + + // Remove all watches + for path := range w.watches { + w.RemoveWatch(path) + } + + // Send "quit" message to the reader goroutine + w.done <- true + + return nil +} + +// AddWatch adds path to the watched file set. +// The flags are interpreted as described in inotify_add_watch(2). +func (w *Watcher) addWatch(path string, flags uint32) error { + if w.isClosed { + return errors.New("inotify instance already closed") + } + + w.mu.Lock() + watchEntry, found := w.watches[path] + w.mu.Unlock() + if found { + watchEntry.flags |= flags + flags |= syscall.IN_MASK_ADD + } + wd, errno := syscall.InotifyAddWatch(w.fd, path, flags) + if wd == -1 { + return errno + } + + w.mu.Lock() + w.watches[path] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = path + w.mu.Unlock() + + return nil +} + +// Watch adds path to the watched file set, watching all events. +func (w *Watcher) watch(path string) error { + return w.addWatch(path, sys_AGNOSTIC_EVENTS) +} + +// RemoveWatch removes path from the watched file set. +func (w *Watcher) removeWatch(path string) error { + w.mu.Lock() + defer w.mu.Unlock() + watch, ok := w.watches[path] + if !ok { + return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path)) + } + success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) + if success == -1 { + return os.NewSyscallError("inotify_rm_watch", errno) + } + delete(w.watches, path) + return nil +} + +// readEvents reads from the inotify file descriptor, converts the +// received events into Event objects and sends them via the Event channel +func (w *Watcher) readEvents() { + var ( + buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events + n int // Number of bytes read with read() + errno error // Syscall errno + ) + + for { + // See if there is a message on the "done" channel + select { + case <-w.done: + syscall.Close(w.fd) + close(w.internalEvent) + close(w.Error) + return + default: + } + + n, errno = syscall.Read(w.fd, buf[:]) + + // If EOF is received + if n == 0 { + syscall.Close(w.fd) + close(w.internalEvent) + close(w.Error) + return + } + + if n < 0 { + w.Error <- os.NewSyscallError("read", errno) + continue + } + if n < syscall.SizeofInotifyEvent { + w.Error <- errors.New("inotify: short read in readEvents()") + continue + } + + var offset uint32 = 0 + // We don't know how many events we just read into the buffer + // While the offset points to at least one whole event... + for offset <= uint32(n-syscall.SizeofInotifyEvent) { + // Point "raw" to the event in the buffer + raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) + event := new(FileEvent) + event.mask = uint32(raw.Mask) + event.cookie = uint32(raw.Cookie) + nameLen := uint32(raw.Len) + // If the event happened to the watched directory or the watched file, the kernel + // doesn't append the filename to the event, but we would like to always fill the + // the "Name" field with a valid filename. We retrieve the path of the watch from + // the "paths" map. + w.mu.Lock() + event.Name = w.paths[int(raw.Wd)] + w.mu.Unlock() + watchedName := event.Name + if nameLen > 0 { + // Point "bytes" at the first byte of the filename + bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) + // The filename is padded with NUL bytes. TrimRight() gets rid of those. + event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") + } + + // Send the events that are not ignored on the events channel + if !event.ignoreLinux() { + // Setup FSNotify flags (inherit from directory watch) + w.fsnmut.Lock() + if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound { + if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound { + w.fsnFlags[event.Name] = fsnFlags + } else { + w.fsnFlags[event.Name] = FSN_ALL + } + } + w.fsnmut.Unlock() + + w.internalEvent <- event + } + + // Move to the next event in the buffer + offset += syscall.SizeofInotifyEvent + nameLen + } + } +} + +// Certain types of events can be "ignored" and not sent over the Event +// channel. Such as events marked ignore by the kernel, or MODIFY events +// against files that do not exist. +func (e *FileEvent) ignoreLinux() bool { + // Ignore anything the inotify API says to ignore + if e.mask&sys_IN_IGNORED == sys_IN_IGNORED { + return true + } + + // If the event is not a DELETE or RENAME, the file must exist. + // Otherwise the event is ignored. + // *Note*: this was put in place because it was seen that a MODIFY + // event was sent after the DELETE. This ignores that MODIFY and + // assumes a DELETE will come or has come if the file doesn't exist. + if !(e.IsDelete() || e.IsRename()) { + _, statErr := os.Lstat(e.Name) + return os.IsNotExist(statErr) + } + return false +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go new file mode 100644 index 000000000..37ea998d0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go @@ -0,0 +1,11 @@ +// Copyright 2013 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 freebsd openbsd netbsd + +package fsnotify + +import "syscall" + +const open_FLAGS = syscall.O_NONBLOCK | syscall.O_RDONLY diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go new file mode 100644 index 000000000..d450318e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go @@ -0,0 +1,11 @@ +// Copyright 2013 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 + +package fsnotify + +import "syscall" + +const open_FLAGS = syscall.O_EVTONLY diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go new file mode 100644 index 000000000..39061f844 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go @@ -0,0 +1,74 @@ +// 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 freebsd openbsd netbsd darwin linux + +package fsnotify + +import ( + "os" + "path/filepath" + "testing" + "time" +) + +func TestFsnotifyFakeSymlink(t *testing.T) { + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + var errorsReceived counter + // Receive errors on the error channel on a separate goroutine + go func() { + for errors := range watcher.Error { + t.Logf("Received error: %s", errors) + errorsReceived.increment() + } + }() + + // Count the CREATE events received + var createEventsReceived, otherEventsReceived counter + go func() { + for ev := range watcher.Event { + t.Logf("event received: %s", ev) + if ev.IsCreate() { + createEventsReceived.increment() + } else { + otherEventsReceived.increment() + } + } + }() + + addWatch(t, watcher, testDir) + + if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil { + t.Fatalf("Failed to create bogus symlink: %s", err) + } + t.Logf("Created bogus symlink") + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + + // Should not be error, just no events for broken links (watching nothing) + if errorsReceived.value() > 0 { + t.Fatal("fsnotify errors have been received.") + } + if otherEventsReceived.value() > 0 { + t.Fatal("fsnotify other events received on the broken link") + } + + // Except for 1 create event (for the link itself) + if createEventsReceived.value() == 0 { + t.Fatal("fsnotify create events were not received after 500 ms") + } + if createEventsReceived.value() > 1 { + t.Fatal("fsnotify more create events received than expected") + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go new file mode 100644 index 000000000..3f5a6487f --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go @@ -0,0 +1,1010 @@ +// 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 fsnotify + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "sync/atomic" + "testing" + "time" +) + +// An atomic counter +type counter struct { + val int32 +} + +func (c *counter) increment() { + atomic.AddInt32(&c.val, 1) +} + +func (c *counter) value() int32 { + return atomic.LoadInt32(&c.val) +} + +func (c *counter) reset() { + atomic.StoreInt32(&c.val, 0) +} + +// tempMkdir makes a temporary directory +func tempMkdir(t *testing.T) string { + dir, err := ioutil.TempDir("", "fsnotify") + if err != nil { + t.Fatalf("failed to create test directory: %s", err) + } + return dir +} + +// newWatcher initializes an fsnotify Watcher instance. +func newWatcher(t *testing.T) *Watcher { + watcher, err := NewWatcher() + if err != nil { + t.Fatalf("NewWatcher() failed: %s", err) + } + return watcher +} + +// addWatch adds a watch for a directory +func addWatch(t *testing.T, watcher *Watcher, dir string) { + if err := watcher.Watch(dir); err != nil { + t.Fatalf("watcher.Watch(%q) failed: %s", dir, err) + } +} + +func TestFsnotifyMultipleOperations(t *testing.T) { + watcher := newWatcher(t) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create directory that's not watched + testDirToMoveFiles := tempMkdir(t) + defer os.RemoveAll(testDirToMoveFiles) + + testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") + testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile") + + addWatch(t, watcher, testDir) + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var createReceived, modifyReceived, deleteReceived, renameReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { + t.Logf("event received: %s", event) + if event.IsDelete() { + deleteReceived.increment() + } + if event.IsModify() { + modifyReceived.increment() + } + if event.IsCreate() { + createReceived.increment() + } + if event.IsRename() { + renameReceived.increment() + } + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + time.Sleep(time.Millisecond) + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + if err := testRename(testFile, testFileRenamed); err != nil { + t.Fatalf("rename failed: %s", err) + } + + // Modify the file outside of the watched dir + f, err = os.Open(testFileRenamed) + if err != nil { + t.Fatalf("open test renamed file failed: %s", err) + } + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // Recreate the file that was moved + f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Close() + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + cReceived := createReceived.value() + if cReceived != 2 { + t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) + } + mReceived := modifyReceived.value() + if mReceived != 1 { + t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) + } + dReceived := deleteReceived.value() + rReceived := renameReceived.value() + if dReceived+rReceived != 1 { + t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1) + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } +} + +func TestFsnotifyMultipleCreates(t *testing.T) { + watcher := newWatcher(t) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") + + addWatch(t, watcher, testDir) + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var createReceived, modifyReceived, deleteReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { + t.Logf("event received: %s", event) + if event.IsDelete() { + deleteReceived.increment() + } + if event.IsCreate() { + createReceived.increment() + } + if event.IsModify() { + modifyReceived.increment() + } + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + time.Sleep(time.Millisecond) + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + os.Remove(testFile) + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // Recreate the file + f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Close() + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // Modify + f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + time.Sleep(time.Millisecond) + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // Modify + f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + time.Sleep(time.Millisecond) + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + cReceived := createReceived.value() + if cReceived != 2 { + t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) + } + mReceived := modifyReceived.value() + if mReceived < 3 { + t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3) + } + dReceived := deleteReceived.value() + if dReceived != 1 { + t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1) + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } +} + +func TestFsnotifyDirOnly(t *testing.T) { + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create a file before watching directory + // This should NOT add any events to the fsnotify event queue + testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") + { + var f *os.File + f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + } + + addWatch(t, watcher, testDir) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile") + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var createReceived, modifyReceived, deleteReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) { + t.Logf("event received: %s", event) + if event.IsDelete() { + deleteReceived.increment() + } + if event.IsModify() { + modifyReceived.increment() + } + if event.IsCreate() { + createReceived.increment() + } + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + time.Sleep(time.Millisecond) + f.WriteString("data") + f.Sync() + f.Close() + + time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete + + os.Remove(testFile) + os.Remove(testFileAlreadyExists) + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + cReceived := createReceived.value() + if cReceived != 1 { + t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1) + } + mReceived := modifyReceived.value() + if mReceived != 1 { + t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) + } + dReceived := deleteReceived.value() + if dReceived != 2 { + t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } +} + +func TestFsnotifyDeleteWatchedDir(t *testing.T) { + watcher := newWatcher(t) + defer watcher.Close() + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create a file before watching directory + testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") + { + var f *os.File + f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + } + + addWatch(t, watcher, testDir) + + // Add a watch for testFile + addWatch(t, watcher, testFileAlreadyExists) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var deleteReceived counter + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) { + t.Logf("event received: %s", event) + if event.IsDelete() { + deleteReceived.increment() + } + } else { + t.Logf("unexpected event received: %s", event) + } + } + }() + + os.RemoveAll(testDir) + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + dReceived := deleteReceived.value() + if dReceived < 2 { + t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived) + } +} + +func TestFsnotifySubDir(t *testing.T) { + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile") + testSubDir := filepath.Join(testDir, "sub") + testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile") + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var createReceived, deleteReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) { + t.Logf("event received: %s", event) + if event.IsCreate() { + createReceived.increment() + } + if event.IsDelete() { + deleteReceived.increment() + } + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + addWatch(t, watcher, testDir) + + // Create sub-directory + if err := os.Mkdir(testSubDir, 0777); err != nil { + t.Fatalf("failed to create test sub-directory: %s", err) + } + + // Create a file + var f *os.File + f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + + // Create a file (Should not see this! we are not watching subdir) + var fs *os.File + fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + fs.Sync() + fs.Close() + + time.Sleep(200 * time.Millisecond) + + // Make sure receive deletes for both file and sub-directory + os.RemoveAll(testSubDir) + os.Remove(testFile1) + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + cReceived := createReceived.value() + if cReceived != 2 { + t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) + } + dReceived := deleteReceived.value() + if dReceived != 2 { + t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } +} + +func TestFsnotifyRename(t *testing.T) { + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + addWatch(t, watcher, testDir) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile") + testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var renameReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { + if event.IsRename() { + renameReceived.increment() + } + t.Logf("event received: %s", event) + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + f.WriteString("data") + f.Sync() + f.Close() + + // Add a watch for testFile + addWatch(t, watcher, testFile) + + if err := testRename(testFile, testFileRenamed); err != nil { + t.Fatalf("rename failed: %s", err) + } + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + if renameReceived.value() == 0 { + t.Fatal("fsnotify rename events have not been received after 500 ms") + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } + + os.Remove(testFileRenamed) +} + +func TestFsnotifyRenameToCreate(t *testing.T) { + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create directory to get file + testDirFrom := tempMkdir(t) + defer os.RemoveAll(testDirFrom) + + addWatch(t, watcher, testDir) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") + testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var createReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { + if event.IsCreate() { + createReceived.increment() + } + t.Logf("event received: %s", event) + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + + if err := testRename(testFile, testFileRenamed); err != nil { + t.Fatalf("rename failed: %s", err) + } + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + if createReceived.value() == 0 { + t.Fatal("fsnotify create events have not been received after 500 ms") + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } + + os.Remove(testFileRenamed) +} + +func TestFsnotifyRenameToOverwrite(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS) + } + + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create directory to get file + testDirFrom := tempMkdir(t) + defer os.RemoveAll(testDirFrom) + + testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") + testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") + + // Create a file + var fr *os.File + fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + fr.Sync() + fr.Close() + + addWatch(t, watcher, testDir) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + var eventReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testFileRenamed) { + eventReceived.increment() + t.Logf("event received: %s", event) + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + + if err := testRename(testFile, testFileRenamed); err != nil { + t.Fatalf("rename failed: %s", err) + } + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + time.Sleep(500 * time.Millisecond) + if eventReceived.value() == 0 { + t.Fatal("fsnotify events have not been received after 500 ms") + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(2 * time.Second): + t.Fatal("event stream was not closed after 2 seconds") + } + + os.Remove(testFileRenamed) +} + +func TestRemovalOfWatch(t *testing.T) { + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Create a file before watching directory + testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") + { + var f *os.File + f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + } + + watcher := newWatcher(t) + defer watcher.Close() + + addWatch(t, watcher, testDir) + if err := watcher.RemoveWatch(testDir); err != nil { + t.Fatalf("Could not remove the watch: %v\n", err) + } + + go func() { + select { + case ev := <-watcher.Event: + t.Fatalf("We received event: %v\n", ev) + case <-time.After(500 * time.Millisecond): + t.Log("No event received, as expected.") + } + }() + + time.Sleep(200 * time.Millisecond) + // Modify the file outside of the watched dir + f, err := os.Open(testFileAlreadyExists) + if err != nil { + t.Fatalf("Open test file failed: %s", err) + } + f.WriteString("data") + f.Sync() + f.Close() + if err := os.Chmod(testFileAlreadyExists, 0700); err != nil { + t.Fatalf("chmod failed: %s", err) + } + time.Sleep(400 * time.Millisecond) +} + +func TestFsnotifyAttrib(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("attributes don't work on Windows.") + } + + watcher := newWatcher(t) + + // Create directory to watch + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile") + + // Receive events on the event channel on a separate goroutine + eventstream := watcher.Event + // The modifyReceived counter counts IsModify events that are not IsAttrib, + // and the attribReceived counts IsAttrib events (which are also IsModify as + // a consequence). + var modifyReceived counter + var attribReceived counter + done := make(chan bool) + go func() { + for event := range eventstream { + // Only count relevant events + if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { + if event.IsModify() { + modifyReceived.increment() + } + if event.IsAttrib() { + attribReceived.increment() + } + t.Logf("event received: %s", event) + } else { + t.Logf("unexpected event received: %s", event) + } + } + done <- true + }() + + // Create a file + // This should add at least one event to the fsnotify event queue + var f *os.File + f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + + f.WriteString("data") + f.Sync() + f.Close() + + // Add a watch for testFile + addWatch(t, watcher, testFile) + + if err := os.Chmod(testFile, 0700); err != nil { + t.Fatalf("chmod failed: %s", err) + } + + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure + // Creating/writing a file changes also the mtime, so IsAttrib should be set to true here + time.Sleep(500 * time.Millisecond) + if modifyReceived.value() == 0 { + t.Fatal("fsnotify modify events have not received after 500 ms") + } + if attribReceived.value() == 0 { + t.Fatal("fsnotify attribute events have not received after 500 ms") + } + + // Modifying the contents of the file does not set the attrib flag (although eg. the mtime + // might have been modified). + modifyReceived.reset() + attribReceived.reset() + + f, err = os.OpenFile(testFile, os.O_WRONLY, 0) + if err != nil { + t.Fatalf("reopening test file failed: %s", err) + } + + f.WriteString("more data") + f.Sync() + f.Close() + + time.Sleep(500 * time.Millisecond) + + if modifyReceived.value() != 1 { + t.Fatal("didn't receive a modify event after changing test file contents") + } + + if attribReceived.value() != 0 { + t.Fatal("did receive an unexpected attrib event after changing test file contents") + } + + modifyReceived.reset() + attribReceived.reset() + + // Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents + // of the file are not changed though) + if err := os.Chmod(testFile, 0600); err != nil { + t.Fatalf("chmod failed: %s", err) + } + + time.Sleep(500 * time.Millisecond) + + if attribReceived.value() != 1 { + t.Fatal("didn't receive an attribute change after 500ms") + } + + // Try closing the fsnotify instance + t.Log("calling Close()") + watcher.Close() + t.Log("waiting for the event channel to become closed...") + select { + case <-done: + t.Log("event channel closed") + case <-time.After(1e9): + t.Fatal("event stream was not closed after 1 second") + } + + os.Remove(testFile) +} + +func TestFsnotifyClose(t *testing.T) { + watcher := newWatcher(t) + watcher.Close() + + var done int32 + go func() { + watcher.Close() + atomic.StoreInt32(&done, 1) + }() + + time.Sleep(50e6) // 50 ms + if atomic.LoadInt32(&done) == 0 { + t.Fatal("double Close() test failed: second Close() call didn't return") + } + + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + if err := watcher.Watch(testDir); err == nil { + t.Fatal("expected error on Watch() after Close(), got nil") + } +} + +func testRename(file1, file2 string) error { + switch runtime.GOOS { + case "windows", "plan9": + return os.Rename(file1, file2) + default: + cmd := exec.Command("mv", file1, file2) + return cmd.Run() + } +} diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go new file mode 100644 index 000000000..d88ae6340 --- /dev/null +++ b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go @@ -0,0 +1,598 @@ +// Copyright 2011 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 fsnotify + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "sync" + "syscall" + "unsafe" +) + +const ( + // Options for AddWatch + sys_FS_ONESHOT = 0x80000000 + sys_FS_ONLYDIR = 0x1000000 + + // Events + sys_FS_ACCESS = 0x1 + sys_FS_ALL_EVENTS = 0xfff + sys_FS_ATTRIB = 0x4 + sys_FS_CLOSE = 0x18 + sys_FS_CREATE = 0x100 + sys_FS_DELETE = 0x200 + sys_FS_DELETE_SELF = 0x400 + sys_FS_MODIFY = 0x2 + sys_FS_MOVE = 0xc0 + sys_FS_MOVED_FROM = 0x40 + sys_FS_MOVED_TO = 0x80 + sys_FS_MOVE_SELF = 0x800 + + // Special events + sys_FS_IGNORED = 0x8000 + sys_FS_Q_OVERFLOW = 0x4000 +) + +const ( + // TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+ + sys_ERROR_MORE_DATA syscall.Errno = 234 +) + +// Event is the type of the notification messages +// received on the watcher's Event channel. +type FileEvent struct { + mask uint32 // Mask of events + cookie uint32 // Unique cookie associating related events (for rename) + Name string // File name (optional) +} + +// IsCreate reports whether the FileEvent was triggered by a creation +func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE } + +// IsDelete reports whether the FileEvent was triggered by a delete +func (e *FileEvent) IsDelete() bool { + return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF) +} + +// IsModify reports whether the FileEvent was triggered by a file modification or attribute change +func (e *FileEvent) IsModify() bool { + return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB) +} + +// IsRename reports whether the FileEvent was triggered by a change name +func (e *FileEvent) IsRename() bool { + return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO) +} + +// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata. +func (e *FileEvent) IsAttrib() bool { + return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB +} + +const ( + opAddWatch = iota + opRemoveWatch +) + +const ( + provisional uint64 = 1 << (32 + iota) +) + +type input struct { + op int + path string + flags uint32 + reply chan error +} + +type inode struct { + handle syscall.Handle + volume uint32 + index uint64 +} + +type watch struct { + ov syscall.Overlapped + ino *inode // i-number + path string // Directory path + mask uint64 // Directory itself is being watched with these notify flags + names map[string]uint64 // Map of names being watched and their notify flags + rename string // Remembers the old name while renaming a file + buf [4096]byte +} + +type indexMap map[uint64]*watch +type watchMap map[uint32]indexMap + +// A Watcher waits for and receives event notifications +// for a specific set of files and directories. +type Watcher struct { + mu sync.Mutex // Map access + port syscall.Handle // Handle to completion port + watches watchMap // Map of watches (key: i-number) + fsnFlags map[string]uint32 // Map of watched files to flags used for filter + fsnmut sync.Mutex // Protects access to fsnFlags. + input chan *input // Inputs to the reader are sent on this channel + internalEvent chan *FileEvent // Events are queued on this channel + Event chan *FileEvent // Events are returned on this channel + Error chan error // Errors are sent on this channel + isClosed bool // Set to true when Close() is first called + quit chan chan<- error + cookie uint32 +} + +// NewWatcher creates and returns a Watcher. +func NewWatcher() (*Watcher, error) { + port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) + if e != nil { + return nil, os.NewSyscallError("CreateIoCompletionPort", e) + } + w := &Watcher{ + port: port, + watches: make(watchMap), + fsnFlags: make(map[string]uint32), + input: make(chan *input, 1), + Event: make(chan *FileEvent, 50), + internalEvent: make(chan *FileEvent), + Error: make(chan error), + quit: make(chan chan<- error, 1), + } + go w.readEvents() + go w.purgeEvents() + return w, nil +} + +// Close closes a Watcher. +// It sends a message to the reader goroutine to quit and removes all watches +// associated with the watcher. +func (w *Watcher) Close() error { + if w.isClosed { + return nil + } + w.isClosed = true + + // Send "quit" message to the reader goroutine + ch := make(chan error) + w.quit <- ch + if err := w.wakeupReader(); err != nil { + return err + } + return <-ch +} + +// AddWatch adds path to the watched file set. +func (w *Watcher) AddWatch(path string, flags uint32) error { + if w.isClosed { + return errors.New("watcher already closed") + } + in := &input{ + op: opAddWatch, + path: filepath.Clean(path), + flags: flags, + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +// Watch adds path to the watched file set, watching all events. +func (w *Watcher) watch(path string) error { + return w.AddWatch(path, sys_FS_ALL_EVENTS) +} + +// RemoveWatch removes path from the watched file set. +func (w *Watcher) removeWatch(path string) error { + in := &input{ + op: opRemoveWatch, + path: filepath.Clean(path), + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +func (w *Watcher) wakeupReader() error { + e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) + if e != nil { + return os.NewSyscallError("PostQueuedCompletionStatus", e) + } + return nil +} + +func getDir(pathname string) (dir string, err error) { + attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) + if e != nil { + return "", os.NewSyscallError("GetFileAttributes", e) + } + if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + dir = pathname + } else { + dir, _ = filepath.Split(pathname) + dir = filepath.Clean(dir) + } + return +} + +func getIno(path string) (ino *inode, err error) { + h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), + syscall.FILE_LIST_DIRECTORY, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) + if e != nil { + return nil, os.NewSyscallError("CreateFile", e) + } + var fi syscall.ByHandleFileInformation + if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { + syscall.CloseHandle(h) + return nil, os.NewSyscallError("GetFileInformationByHandle", e) + } + ino = &inode{ + handle: h, + volume: fi.VolumeSerialNumber, + index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), + } + return ino, nil +} + +// Must run within the I/O thread. +func (m watchMap) get(ino *inode) *watch { + if i := m[ino.volume]; i != nil { + return i[ino.index] + } + return nil +} + +// Must run within the I/O thread. +func (m watchMap) set(ino *inode, watch *watch) { + i := m[ino.volume] + if i == nil { + i = make(indexMap) + m[ino.volume] = i + } + i[ino.index] = watch +} + +// Must run within the I/O thread. +func (w *Watcher) addWatch(pathname string, flags uint64) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + if flags&sys_FS_ONLYDIR != 0 && pathname != dir { + return nil + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watchEntry := w.watches.get(ino) + w.mu.Unlock() + if watchEntry == nil { + if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { + syscall.CloseHandle(ino.handle) + return os.NewSyscallError("CreateIoCompletionPort", e) + } + watchEntry = &watch{ + ino: ino, + path: dir, + names: make(map[string]uint64), + } + w.mu.Lock() + w.watches.set(ino, watchEntry) + w.mu.Unlock() + flags |= provisional + } else { + syscall.CloseHandle(ino.handle) + } + if pathname == dir { + watchEntry.mask |= flags + } else { + watchEntry.names[filepath.Base(pathname)] |= flags + } + if err = w.startRead(watchEntry); err != nil { + return err + } + if pathname == dir { + watchEntry.mask &= ^provisional + } else { + watchEntry.names[filepath.Base(pathname)] &= ^provisional + } + return nil +} + +// Must run within the I/O thread. +func (w *Watcher) remWatch(pathname string) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watch := w.watches.get(ino) + w.mu.Unlock() + if watch == nil { + return fmt.Errorf("can't remove non-existent watch for: %s", pathname) + } + if pathname == dir { + w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) + watch.mask = 0 + } else { + name := filepath.Base(pathname) + w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED) + delete(watch.names, name) + } + return w.startRead(watch) +} + +// Must run within the I/O thread. +func (w *Watcher) deleteWatch(watch *watch) { + for name, mask := range watch.names { + if mask&provisional == 0 { + w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED) + } + delete(watch.names, name) + } + if watch.mask != 0 { + if watch.mask&provisional == 0 { + w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) + } + watch.mask = 0 + } +} + +// Must run within the I/O thread. +func (w *Watcher) startRead(watch *watch) error { + if e := syscall.CancelIo(watch.ino.handle); e != nil { + w.Error <- os.NewSyscallError("CancelIo", e) + w.deleteWatch(watch) + } + mask := toWindowsFlags(watch.mask) + for _, m := range watch.names { + mask |= toWindowsFlags(m) + } + if mask == 0 { + if e := syscall.CloseHandle(watch.ino.handle); e != nil { + w.Error <- os.NewSyscallError("CloseHandle", e) + } + w.mu.Lock() + delete(w.watches[watch.ino.volume], watch.ino.index) + w.mu.Unlock() + return nil + } + e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], + uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) + if e != nil { + err := os.NewSyscallError("ReadDirectoryChanges", e) + if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { + // Watched directory was probably removed + if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) { + if watch.mask&sys_FS_ONESHOT != 0 { + watch.mask = 0 + } + } + err = nil + } + w.deleteWatch(watch) + w.startRead(watch) + return err + } + return nil +} + +// readEvents reads from the I/O completion port, converts the +// received events into Event objects and sends them via the Event channel. +// Entry point to the I/O thread. +func (w *Watcher) readEvents() { + var ( + n, key uint32 + ov *syscall.Overlapped + ) + runtime.LockOSThread() + + for { + e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) + watch := (*watch)(unsafe.Pointer(ov)) + + if watch == nil { + select { + case ch := <-w.quit: + w.mu.Lock() + var indexes []indexMap + for _, index := range w.watches { + indexes = append(indexes, index) + } + w.mu.Unlock() + for _, index := range indexes { + for _, watch := range index { + w.deleteWatch(watch) + w.startRead(watch) + } + } + var err error + if e := syscall.CloseHandle(w.port); e != nil { + err = os.NewSyscallError("CloseHandle", e) + } + close(w.internalEvent) + close(w.Error) + ch <- err + return + case in := <-w.input: + switch in.op { + case opAddWatch: + in.reply <- w.addWatch(in.path, uint64(in.flags)) + case opRemoveWatch: + in.reply <- w.remWatch(in.path) + } + default: + } + continue + } + + switch e { + case sys_ERROR_MORE_DATA: + if watch == nil { + w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") + } else { + // The i/o succeeded but the buffer is full. + // In theory we should be building up a full packet. + // In practice we can get away with just carrying on. + n = uint32(unsafe.Sizeof(watch.buf)) + } + case syscall.ERROR_ACCESS_DENIED: + // Watched directory was probably removed + w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) + w.deleteWatch(watch) + w.startRead(watch) + continue + case syscall.ERROR_OPERATION_ABORTED: + // CancelIo was called on this handle + continue + default: + w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e) + continue + case nil: + } + + var offset uint32 + for { + if n == 0 { + w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW} + w.Error <- errors.New("short read in readEvents()") + break + } + + // Point "raw" to the event in the buffer + raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) + buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) + name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) + fullname := watch.path + "\\" + name + + var mask uint64 + switch raw.Action { + case syscall.FILE_ACTION_REMOVED: + mask = sys_FS_DELETE_SELF + case syscall.FILE_ACTION_MODIFIED: + mask = sys_FS_MODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + watch.rename = name + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + if watch.names[watch.rename] != 0 { + watch.names[name] |= watch.names[watch.rename] + delete(watch.names, watch.rename) + mask = sys_FS_MOVE_SELF + } + } + + sendNameEvent := func() { + if w.sendEvent(fullname, watch.names[name]&mask) { + if watch.names[name]&sys_FS_ONESHOT != 0 { + delete(watch.names, name) + } + } + } + if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { + sendNameEvent() + } + if raw.Action == syscall.FILE_ACTION_REMOVED { + w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED) + delete(watch.names, name) + } + if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { + if watch.mask&sys_FS_ONESHOT != 0 { + watch.mask = 0 + } + } + if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { + fullname = watch.path + "\\" + watch.rename + sendNameEvent() + } + + // Move to the next event in the buffer + if raw.NextEntryOffset == 0 { + break + } + offset += raw.NextEntryOffset + + // Error! + if offset >= n { + w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") + break + } + } + + if err := w.startRead(watch); err != nil { + w.Error <- err + } + } +} + +func (w *Watcher) sendEvent(name string, mask uint64) bool { + if mask == 0 { + return false + } + event := &FileEvent{mask: uint32(mask), Name: name} + if mask&sys_FS_MOVE != 0 { + if mask&sys_FS_MOVED_FROM != 0 { + w.cookie++ + } + event.cookie = w.cookie + } + select { + case ch := <-w.quit: + w.quit <- ch + case w.Event <- event: + } + return true +} + +func toWindowsFlags(mask uint64) uint32 { + var m uint32 + if mask&sys_FS_ACCESS != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS + } + if mask&sys_FS_MODIFY != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE + } + if mask&sys_FS_ATTRIB != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES + } + if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME + } + return m +} + +func toFSnotifyFlags(action uint32) uint64 { + switch action { + case syscall.FILE_ACTION_ADDED: + return sys_FS_CREATE + case syscall.FILE_ACTION_REMOVED: + return sys_FS_DELETE + case syscall.FILE_ACTION_MODIFIED: + return sys_FS_MODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + return sys_FS_MOVED_FROM + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + return sys_FS_MOVED_TO + } + return 0 +} diff --git a/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/LICENSE b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/LICENSE new file mode 100644 index 000000000..249514b0f --- /dev/null +++ b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/LICENSE @@ -0,0 +1,13 @@ + Copyright 2013 John Howard Palevich + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/README.md b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/README.md new file mode 100644 index 000000000..15f72fd81 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/README.md @@ -0,0 +1,48 @@ +go-nat-pmp +========== + +A Go language client for the NAT-PMP internet protocol for port mapping and discovering the external +IP address of a firewall. + +NAT-PMP is supported by Apple brand routers and open source routers like Tomato and DD-WRT. + +See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03 + +Get the package +--------------- + + go get -u github.com/jackpal/go-nat-pmp + +Usage +----- + + import natpmp "github.com/jackpal/go-nat-pmp" + + client := natpmp.NewClient(gatewayIP) + response, err := client.GetExternalAddress() + if err != nil { + return + } + print("External IP address:", response.ExternalIPAddress) + +Notes +----- + +There doesn't seem to be an easy way to programmatically determine the address of the default gateway. +(Linux and OSX have a "routes" kernel API that can be examined to get this information, but there is +no Go package for getting this information.) + +Clients +------- + +This library is used in the Taipei Torrent BitTorrent client http://github.com/jackpal/Taipei-Torrent + +Complete documentation +---------------------- + + http://godoc.org/github.com/jackpal/go-nat-pmp + +License +------- + +This project is licensed under the Apache License 2.0. diff --git a/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go new file mode 100644 index 000000000..8ce4e8342 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go @@ -0,0 +1,184 @@ +package natpmp + +import ( + "fmt" + "log" + "net" + "time" +) + +// Implement the NAT-PMP protocol, typically supported by Apple routers and open source +// routers such as DD-WRT and Tomato. +// +// See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03 +// +// Usage: +// +// client := natpmp.NewClient(gatewayIP) +// response, err := client.GetExternalAddress() + +const nAT_PMP_PORT = 5351 + +const nAT_TRIES = 9 + +const nAT_INITIAL_MS = 250 + +// The recommended mapping lifetime for AddPortMapping +const RECOMMENDED_MAPPING_LIFETIME_SECONDS = 3600 + +// Client is a NAT-PMP protocol client. +type Client struct { + gateway net.IP +} + +// Create a NAT-PMP client for the NAT-PMP server at the gateway. +func NewClient(gateway net.IP) (nat *Client) { + return &Client{gateway} +} + +// Results of the NAT-PMP GetExternalAddress operation +type GetExternalAddressResult struct { + SecondsSinceStartOfEpoc uint32 + ExternalIPAddress [4]byte +} + +// Get the external address of the router. +func (n *Client) GetExternalAddress() (result *GetExternalAddressResult, err error) { + msg := make([]byte, 2) + msg[0] = 0 // Version 0 + msg[1] = 0 // OP Code 0 + response, err := n.rpc(msg, 12) + if err != nil { + return + } + result = &GetExternalAddressResult{} + result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8]) + copy(result.ExternalIPAddress[:], response[8:12]) + return +} + +// Results of the NAT-PMP AddPortMapping operation +type AddPortMappingResult struct { + SecondsSinceStartOfEpoc uint32 + InternalPort uint16 + MappedExternalPort uint16 + PortMappingLifetimeInSeconds uint32 +} + +// Add (or delete) a port mapping. To delete a mapping, set the requestedExternalPort and lifetime to 0 +func (n *Client) AddPortMapping(protocol string, internalPort, requestedExternalPort int, lifetime int) (result *AddPortMappingResult, err error) { + var opcode byte + if protocol == "udp" { + opcode = 1 + } else if protocol == "tcp" { + opcode = 2 + } else { + err = fmt.Errorf("unknown protocol %v", protocol) + return + } + msg := make([]byte, 12) + msg[0] = 0 // Version 0 + msg[1] = opcode + writeNetworkOrderUint16(msg[4:6], uint16(internalPort)) + writeNetworkOrderUint16(msg[6:8], uint16(requestedExternalPort)) + writeNetworkOrderUint32(msg[8:12], uint32(lifetime)) + response, err := n.rpc(msg, 16) + if err != nil { + return + } + result = &AddPortMappingResult{} + result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8]) + result.InternalPort = readNetworkOrderUint16(response[8:10]) + result.MappedExternalPort = readNetworkOrderUint16(response[10:12]) + result.PortMappingLifetimeInSeconds = readNetworkOrderUint32(response[12:16]) + return +} + +func (n *Client) rpc(msg []byte, resultSize int) (result []byte, err error) { + var server net.UDPAddr + server.IP = n.gateway + server.Port = nAT_PMP_PORT + conn, err := net.DialUDP("udp", nil, &server) + if err != nil { + return + } + defer conn.Close() + + result = make([]byte, resultSize) + + needNewDeadline := true + + var tries uint + for tries = 0; tries < nAT_TRIES; { + if needNewDeadline { + err = conn.SetDeadline(time.Now().Add((nAT_INITIAL_MS << tries) * time.Millisecond)) + if err != nil { + return + } + needNewDeadline = false + } + _, err = conn.Write(msg) + if err != nil { + return + } + var bytesRead int + var remoteAddr *net.UDPAddr + bytesRead, remoteAddr, err = conn.ReadFromUDP(result) + if err != nil { + if err.(net.Error).Timeout() { + tries++ + needNewDeadline = true + continue + } + return + } + if !remoteAddr.IP.Equal(n.gateway) { + log.Printf("Ignoring packet because IPs differ:", remoteAddr, n.gateway) + // Ignore this packet. + // Continue without increasing retransmission timeout or deadline. + continue + } + if bytesRead != resultSize { + err = fmt.Errorf("unexpected result size %d, expected %d", bytesRead, resultSize) + return + } + if result[0] != 0 { + err = fmt.Errorf("unknown protocol version %d", result[0]) + return + } + expectedOp := msg[1] | 0x80 + if result[1] != expectedOp { + err = fmt.Errorf("Unexpected opcode %d. Expected %d", result[1], expectedOp) + return + } + resultCode := readNetworkOrderUint16(result[2:4]) + if resultCode != 0 { + err = fmt.Errorf("Non-zero result code %d", resultCode) + return + } + // If we got here the RPC is good. + return + } + err = fmt.Errorf("Timed out trying to contact gateway") + return +} + +func writeNetworkOrderUint16(buf []byte, d uint16) { + buf[0] = byte(d >> 8) + buf[1] = byte(d) +} + +func writeNetworkOrderUint32(buf []byte, d uint32) { + buf[0] = byte(d >> 24) + buf[1] = byte(d >> 16) + buf[2] = byte(d >> 8) + buf[3] = byte(d) +} + +func readNetworkOrderUint16(buf []byte) uint16 { + return (uint16(buf[0]) << 8) | uint16(buf[1]) +} + +func readNetworkOrderUint32(buf []byte) uint32 { + return (uint32(buf[0]) << 24) | (uint32(buf[1]) << 16) | (uint32(buf[2]) << 8) | uint32(buf[3]) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/.gitignore b/Godeps/_workspace/src/github.com/obscuren/ecies/.gitignore new file mode 100644 index 000000000..802b6744a --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +*~ diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/LICENSE b/Godeps/_workspace/src/github.com/obscuren/ecies/LICENSE new file mode 100644 index 000000000..e1ed19a27 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2013 Kyle Isom +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/README b/Godeps/_workspace/src/github.com/obscuren/ecies/README new file mode 100644 index 000000000..2650c7b9f --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/README @@ -0,0 +1,94 @@ +# NOTE + +This implementation is direct fork of Kylom's implementation. I claim no authorship over this code apart from some minor modifications. +Please be aware this code **has not yet been reviewed**. + +ecies implements the Elliptic Curve Integrated Encryption Scheme. + +The package is designed to be compliant with the appropriate NIST +standards, and therefore doesn't support the full SEC 1 algorithm set. + + +STATUS: + +ecies should be ready for use. The ASN.1 support is only complete so +far as to supported the listed algorithms before. + + +CAVEATS + +1. CMAC support is currently not present. + + +SUPPORTED ALGORITHMS + + SYMMETRIC CIPHERS HASH FUNCTIONS + AES128 SHA-1 + AES192 SHA-224 + AES256 SHA-256 + SHA-384 + ELLIPTIC CURVE SHA-512 + P256 + P384 KEY DERIVATION FUNCTION + P521 NIST SP 800-65a Concatenation KDF + +Curve P224 isn't supported because it does not provide a minimum security +level of AES128 with HMAC-SHA1. According to NIST SP 800-57, the security +level of P224 is 112 bits of security. Symmetric ciphers use CTR-mode; +message tags are computed using HMAC- function. + + +CURVE SELECTION + +According to NIST SP 800-57, the following curves should be selected: + + +----------------+-------+ + | SYMMETRIC SIZE | CURVE | + +----------------+-------+ + | 128-bit | P256 | + +----------------+-------+ + | 192-bit | P384 | + +----------------+-------+ + | 256-bit | P521 | + +----------------+-------+ + + +TODO + +1. Look at serialising the parameters with the SEC 1 ASN.1 module. +2. Validate ASN.1 formats with SEC 1. + + +TEST VECTORS + +The only test vectors I've found so far date from 1993, predating AES +and including only 163-bit curves. Therefore, there are no published +test vectors to compare to. + + +LICENSE + +ecies is released under the same license as the Go source code. See the +LICENSE file for details. + + +REFERENCES + +* SEC (Standard for Efficient Cryptography) 1, version 2.0: Elliptic + Curve Cryptography; Certicom, May 2009. + http://www.secg.org/sec1-v2.pdf +* GEC (Guidelines for Efficient Cryptography) 2, version 0.3: Test + Vectors for SEC 1; Certicom, September 1999. + http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf +* NIST SP 800-56a: Recommendation for Pair-Wise Key Establishment Schemes + Using Discrete Logarithm Cryptography. National Institute of Standards + and Technology, May 2007. + http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf +* Suite B Implementer’s Guide to NIST SP 800-56A. National Security + Agency, July 28, 2009. + http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf +* NIST SP 800-57: Recommendation for Key Management – Part 1: General + (Revision 3). National Institute of Standards and Technology, July + 2012. + http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf + diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/asn1.go b/Godeps/_workspace/src/github.com/obscuren/ecies/asn1.go new file mode 100644 index 000000000..3ef194ea0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/asn1.go @@ -0,0 +1,556 @@ +package ecies + +import ( + "bytes" + "crypto" + "crypto/elliptic" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/asn1" + "encoding/pem" + "fmt" + "hash" + "math/big" +) + +var ( + secgScheme = []int{1, 3, 132, 1} + shaScheme = []int{2, 16, 840, 1, 101, 3, 4, 2} + ansiX962Scheme = []int{1, 2, 840, 10045} + x963Scheme = []int{1, 2, 840, 63, 0} +) + +var ErrInvalidPrivateKey = fmt.Errorf("ecies: invalid private key") + +func doScheme(base, v []int) asn1.ObjectIdentifier { + var oidInts asn1.ObjectIdentifier + oidInts = append(oidInts, base...) + return append(oidInts, v...) +} + +// curve OID code taken from crypto/x509, including +// - oidNameCurve* +// - namedCurveFromOID +// - oidFromNamedCurve +// RFC 5480, 2.1.1.1. Named Curve +// +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } +// +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// +// NB: secp256r1 is equivalent to prime256v1 +type secgNamedCurve asn1.ObjectIdentifier + +var ( + secgNamedCurveP224 = secgNamedCurve{1, 3, 132, 0, 33} + secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7} + secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34} + secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35} + rawCurveP224 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 3} + rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7} + rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4} + rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5} +) + +func rawCurve(curve elliptic.Curve) []byte { + switch curve { + case elliptic.P224(): + return rawCurveP224 + case elliptic.P256(): + return rawCurveP256 + case elliptic.P384(): + return rawCurveP384 + case elliptic.P521(): + return rawCurveP521 + default: + return nil + } +} + +func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool { + if len(curve) != len(curve2) { + return false + } + for i, _ := range curve { + if curve[i] != curve2[i] { + return false + } + } + return true +} + +func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { + switch { + case curve.Equal(secgNamedCurveP224): + return elliptic.P224() + case curve.Equal(secgNamedCurveP256): + return elliptic.P256() + case curve.Equal(secgNamedCurveP384): + return elliptic.P384() + case curve.Equal(secgNamedCurveP521): + return elliptic.P521() + } + return nil +} + +func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) { + switch curve { + case elliptic.P224(): + return secgNamedCurveP224, true + case elliptic.P256(): + return secgNamedCurveP256, true + case elliptic.P384(): + return secgNamedCurveP384, true + case elliptic.P521(): + return secgNamedCurveP521, true + } + + return nil, false +} + +// asnAlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type asnAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.RawValue `asn1:"optional"` +} + +func (a asnAlgorithmIdentifier) Cmp(b asnAlgorithmIdentifier) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i, _ := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type asnHashFunction asnAlgorithmIdentifier + +var ( + oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} + oidSHA224 = doScheme(shaScheme, []int{4}) + oidSHA256 = doScheme(shaScheme, []int{1}) + oidSHA384 = doScheme(shaScheme, []int{2}) + oidSHA512 = doScheme(shaScheme, []int{3}) +) + +func hashFromOID(oid asn1.ObjectIdentifier) func() hash.Hash { + switch { + case oid.Equal(oidSHA1): + return sha1.New + case oid.Equal(oidSHA224): + return sha256.New224 + case oid.Equal(oidSHA256): + return sha256.New + case oid.Equal(oidSHA384): + return sha512.New384 + case oid.Equal(oidSHA512): + return sha512.New + } + return nil +} + +func oidFromHash(hash crypto.Hash) (asn1.ObjectIdentifier, bool) { + switch hash { + case crypto.SHA1: + return oidSHA1, true + case crypto.SHA224: + return oidSHA224, true + case crypto.SHA256: + return oidSHA256, true + case crypto.SHA384: + return oidSHA384, true + case crypto.SHA512: + return oidSHA512, true + default: + return nil, false + } +} + +var ( + asnAlgoSHA1 = asnHashFunction{ + Algorithm: oidSHA1, + } + asnAlgoSHA224 = asnHashFunction{ + Algorithm: oidSHA224, + } + asnAlgoSHA256 = asnHashFunction{ + Algorithm: oidSHA256, + } + asnAlgoSHA384 = asnHashFunction{ + Algorithm: oidSHA384, + } + asnAlgoSHA512 = asnHashFunction{ + Algorithm: oidSHA512, + } +) + +// type ASNasnSubjectPublicKeyInfo struct { +// +// } +// + +type asnSubjectPublicKeyInfo struct { + Algorithm asn1.ObjectIdentifier + PublicKey asn1.BitString + Supplements ecpksSupplements `asn1:"optional"` +} + +type asnECPKAlgorithms struct { + Type asn1.ObjectIdentifier +} + +var idPublicKeyType = doScheme(ansiX962Scheme, []int{2}) +var idEcPublicKey = doScheme(idPublicKeyType, []int{1}) +var idEcPublicKeySupplemented = doScheme(idPublicKeyType, []int{0}) + +func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) { + switch curve { + case elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(): + raw := rawCurve(curve) + return asn1.RawValue{ + Tag: 30, + Bytes: raw[2:], + FullBytes: raw, + }, true + default: + return rv, false + } +} + +func asnECPublicKeyType(curve elliptic.Curve) (algo asnAlgorithmIdentifier, ok bool) { + raw, ok := curveToRaw(curve) + if !ok { + return + } else { + return asnAlgorithmIdentifier{Algorithm: idEcPublicKey, + Parameters: raw}, true + } +} + +type asnECPrivKeyVer int + +var asnECPrivKeyVer1 asnECPrivKeyVer = 1 + +type asnPrivateKey struct { + Version asnECPrivKeyVer + Private []byte + Curve secgNamedCurve `asn1:"optional"` + Public asn1.BitString +} + +var asnECDH = doScheme(secgScheme, []int{12}) + +type asnECDHAlgorithm asnAlgorithmIdentifier + +var ( + dhSinglePass_stdDH_sha1kdf = asnECDHAlgorithm{ + Algorithm: doScheme(x963Scheme, []int{2}), + } + dhSinglePass_stdDH_sha256kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 1}), + } + dhSinglePass_stdDH_sha384kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 2}), + } + dhSinglePass_stdDH_sha224kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 0}), + } + dhSinglePass_stdDH_sha512kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 3}), + } +) + +func (a asnECDHAlgorithm) Cmp(b asnECDHAlgorithm) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i, _ := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +// asnNISTConcatenation is the only supported KDF at this time. +type asnKeyDerivationFunction asnAlgorithmIdentifier + +var asnNISTConcatenationKDF = asnKeyDerivationFunction{ + Algorithm: doScheme(secgScheme, []int{17, 1}), +} + +func (a asnKeyDerivationFunction) Cmp(b asnKeyDerivationFunction) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i, _ := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +var eciesRecommendedParameters = doScheme(secgScheme, []int{7}) +var eciesSpecifiedParameters = doScheme(secgScheme, []int{8}) + +type asnECIESParameters struct { + KDF asnKeyDerivationFunction `asn1:"optional"` + Sym asnSymmetricEncryption `asn1:"optional"` + MAC asnMessageAuthenticationCode `asn1:"optional"` +} + +type asnSymmetricEncryption asnAlgorithmIdentifier + +var ( + aes128CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 0}), + } + aes192CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 1}), + } + aes256CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 2}), + } +) + +func (a asnSymmetricEncryption) Cmp(b asnSymmetricEncryption) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i, _ := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type asnMessageAuthenticationCode asnAlgorithmIdentifier + +var ( + hmacFull = asnMessageAuthenticationCode{ + Algorithm: doScheme(secgScheme, []int{22}), + } +) + +func (a asnMessageAuthenticationCode) Cmp(b asnMessageAuthenticationCode) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i, _ := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type ecpksSupplements struct { + ECDomain secgNamedCurve + ECCAlgorithms eccAlgorithmSet +} + +type eccAlgorithmSet struct { + ECDH asnECDHAlgorithm `asn1:"optional"` + ECIES asnECIESParameters `asn1:"optional"` +} + +func marshalSubjectPublicKeyInfo(pub *PublicKey) (subj asnSubjectPublicKeyInfo, err error) { + subj.Algorithm = idEcPublicKeySupplemented + curve, ok := oidFromNamedCurve(pub.Curve) + if !ok { + err = ErrInvalidPublicKey + return + } + subj.Supplements.ECDomain = curve + if pub.Params != nil { + subj.Supplements.ECCAlgorithms.ECDH = paramsToASNECDH(pub.Params) + subj.Supplements.ECCAlgorithms.ECIES = paramsToASNECIES(pub.Params) + } + pubkey := elliptic.Marshal(pub.Curve, pub.X, pub.Y) + subj.PublicKey = asn1.BitString{ + BitLength: len(pubkey) * 8, + Bytes: pubkey, + } + return +} + +// Encode a public key to DER format. +func MarshalPublic(pub *PublicKey) ([]byte, error) { + subj, err := marshalSubjectPublicKeyInfo(pub) + if err != nil { + return nil, err + } + return asn1.Marshal(subj) +} + +// Decode a DER-encoded public key. +func UnmarshalPublic(in []byte) (pub *PublicKey, err error) { + var subj asnSubjectPublicKeyInfo + + if _, err = asn1.Unmarshal(in, &subj); err != nil { + return + } + if !subj.Algorithm.Equal(idEcPublicKeySupplemented) { + err = ErrInvalidPublicKey + return + } + pub = new(PublicKey) + pub.Curve = namedCurveFromOID(subj.Supplements.ECDomain) + x, y := elliptic.Unmarshal(pub.Curve, subj.PublicKey.Bytes) + if x == nil { + err = ErrInvalidPublicKey + return + } + pub.X = x + pub.Y = y + pub.Params = new(ECIESParams) + asnECIEStoParams(subj.Supplements.ECCAlgorithms.ECIES, pub.Params) + asnECDHtoParams(subj.Supplements.ECCAlgorithms.ECDH, pub.Params) + if pub.Params == nil { + if pub.Params = ParamsFromCurve(pub.Curve); pub.Params == nil { + err = ErrInvalidPublicKey + } + } + return +} + +func marshalPrivateKey(prv *PrivateKey) (ecprv asnPrivateKey, err error) { + ecprv.Version = asnECPrivKeyVer1 + ecprv.Private = prv.D.Bytes() + + var ok bool + ecprv.Curve, ok = oidFromNamedCurve(prv.PublicKey.Curve) + if !ok { + err = ErrInvalidPrivateKey + return + } + + var pub []byte + if pub, err = MarshalPublic(&prv.PublicKey); err != nil { + return + } else { + ecprv.Public = asn1.BitString{ + BitLength: len(pub) * 8, + Bytes: pub, + } + } + return +} + +// Encode a private key to DER format. +func MarshalPrivate(prv *PrivateKey) ([]byte, error) { + ecprv, err := marshalPrivateKey(prv) + if err != nil { + return nil, err + } + return asn1.Marshal(ecprv) +} + +// Decode a private key from a DER-encoded format. +func UnmarshalPrivate(in []byte) (prv *PrivateKey, err error) { + var ecprv asnPrivateKey + + if _, err = asn1.Unmarshal(in, &ecprv); err != nil { + return + } else if ecprv.Version != asnECPrivKeyVer1 { + err = ErrInvalidPrivateKey + return + } + + privateCurve := namedCurveFromOID(ecprv.Curve) + if privateCurve == nil { + err = ErrInvalidPrivateKey + return + } + + prv = new(PrivateKey) + prv.D = new(big.Int).SetBytes(ecprv.Private) + + if pub, err := UnmarshalPublic(ecprv.Public.Bytes); err != nil { + return nil, err + } else { + prv.PublicKey = *pub + } + + return +} + +// Export a public key to PEM format. +func ExportPublicPEM(pub *PublicKey) (out []byte, err error) { + der, err := MarshalPublic(pub) + if err != nil { + return + } + + var block pem.Block + block.Type = "ELLIPTIC CURVE PUBLIC KEY" + block.Bytes = der + + buf := new(bytes.Buffer) + err = pem.Encode(buf, &block) + if err != nil { + return + } else { + out = buf.Bytes() + } + return +} + +// Export a private key to PEM format. +func ExportPrivatePEM(prv *PrivateKey) (out []byte, err error) { + der, err := MarshalPrivate(prv) + if err != nil { + return + } + + var block pem.Block + block.Type = "ELLIPTIC CURVE PRIVATE KEY" + block.Bytes = der + + buf := new(bytes.Buffer) + err = pem.Encode(buf, &block) + if err != nil { + return + } else { + out = buf.Bytes() + } + return +} + +// Import a PEM-encoded public key. +func ImportPublicPEM(in []byte) (pub *PublicKey, err error) { + p, _ := pem.Decode(in) + if p == nil || p.Type != "ELLIPTIC CURVE PUBLIC KEY" { + return nil, ErrInvalidPublicKey + } + + pub, err = UnmarshalPublic(p.Bytes) + return +} + +// Import a PEM-encoded private key. +func ImportPrivatePEM(in []byte) (prv *PrivateKey, err error) { + p, _ := pem.Decode(in) + if p == nil || p.Type != "ELLIPTIC CURVE PRIVATE KEY" { + return nil, ErrInvalidPrivateKey + } + + prv, err = UnmarshalPrivate(p.Bytes) + return +} diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/ecies.go b/Godeps/_workspace/src/github.com/obscuren/ecies/ecies.go new file mode 100644 index 000000000..0e2403d47 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/ecies.go @@ -0,0 +1,326 @@ +package ecies + +import ( + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/subtle" + "fmt" + "hash" + "io" + "math/big" +) + +var ( + ErrImport = fmt.Errorf("ecies: failed to import key") + ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve") + ErrInvalidParams = fmt.Errorf("ecies: invalid ECIES parameters") + ErrInvalidPublicKey = fmt.Errorf("ecies: invalid public key") + ErrSharedKeyTooBig = fmt.Errorf("ecies: shared key is too big") +) + +// PublicKey is a representation of an elliptic curve public key. +type PublicKey struct { + X *big.Int + Y *big.Int + elliptic.Curve + Params *ECIESParams +} + +// Export an ECIES public key as an ECDSA public key. +func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey { + return &ecdsa.PublicKey{pub.Curve, pub.X, pub.Y} +} + +// Import an ECDSA public key as an ECIES public key. +func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey { + return &PublicKey{ + X: pub.X, + Y: pub.Y, + Curve: pub.Curve, + Params: ParamsFromCurve(pub.Curve), + } +} + +// PrivateKey is a representation of an elliptic curve private key. +type PrivateKey struct { + PublicKey + D *big.Int +} + +// Export an ECIES private key as an ECDSA private key. +func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey { + pub := &prv.PublicKey + pubECDSA := pub.ExportECDSA() + return &ecdsa.PrivateKey{*pubECDSA, prv.D} +} + +// Import an ECDSA private key as an ECIES private key. +func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey { + pub := ImportECDSAPublic(&prv.PublicKey) + return &PrivateKey{*pub, prv.D} +} + +// Generate an elliptic curve public / private keypair. If params is nil, +// the recommended default paramters for the key will be chosen. +func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) { + pb, x, y, err := elliptic.GenerateKey(curve, rand) + if err != nil { + return + } + prv = new(PrivateKey) + prv.PublicKey.X = x + prv.PublicKey.Y = y + prv.PublicKey.Curve = curve + prv.D = new(big.Int).SetBytes(pb) + if params == nil { + params = ParamsFromCurve(curve) + } + prv.PublicKey.Params = params + return +} + +// MaxSharedKeyLength returns the maximum length of the shared key the +// public key can produce. +func MaxSharedKeyLength(pub *PublicKey) int { + return (pub.Curve.Params().BitSize + 7) / 8 +} + +// ECDH key agreement method used to establish secret keys for encryption. +func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) { + if prv.PublicKey.Curve != pub.Curve { + err = ErrInvalidCurve + return + } + x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes()) + if x == nil || (x.BitLen()+7)/8 < (skLen+macLen) { + err = ErrSharedKeyTooBig + return + } + sk = x.Bytes()[:skLen+macLen] + return +} + +var ( + ErrKeyDataTooLong = fmt.Errorf("ecies: can't supply requested key data") + ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long") + ErrInvalidMessage = fmt.Errorf("ecies: invalid message") +) + +var ( + big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil) + big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1)) +) + +func incCounter(ctr []byte) { + if ctr[3]++; ctr[3] != 0 { + return + } else if ctr[2]++; ctr[2] != 0 { + return + } else if ctr[1]++; ctr[1] != 0 { + return + } else if ctr[0]++; ctr[0] != 0 { + return + } + return +} + +// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1). +func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) { + if s1 == nil { + s1 = make([]byte, 0) + } + + reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8) + if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 { + fmt.Println(big2To32M1) + return nil, ErrKeyDataTooLong + } + + counter := []byte{0, 0, 0, 1} + k = make([]byte, 0) + + for i := 0; i <= reps; i++ { + hash.Write(counter) + hash.Write(z) + hash.Write(s1) + k = append(k, hash.Sum(nil)...) + hash.Reset() + incCounter(counter) + } + + k = k[:kdLen] + return +} + +// messageTag computes the MAC of a message (called the tag) as per +// SEC 1, 3.5. +func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte { + if shared == nil { + shared = make([]byte, 0) + } + mac := hmac.New(hash, km) + mac.Write(msg) + tag := mac.Sum(nil) + return tag +} + +// Generate an initialisation vector for CTR mode. +func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) { + iv = make([]byte, params.BlockSize) + _, err = io.ReadFull(rand, iv) + return +} + +// symEncrypt carries out CTR encryption using the block cipher specified in the +// parameters. +func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) { + c, err := params.Cipher(key) + if err != nil { + return + } + + iv, err := generateIV(params, rand) + if err != nil { + return + } + ctr := cipher.NewCTR(c, iv) + + ct = make([]byte, len(m)+params.BlockSize) + copy(ct, iv) + ctr.XORKeyStream(ct[params.BlockSize:], m) + return +} + +// symDecrypt carries out CTR decryption using the block cipher specified in +// the parameters +func symDecrypt(rand io.Reader, params *ECIESParams, key, ct []byte) (m []byte, err error) { + c, err := params.Cipher(key) + if err != nil { + return + } + + ctr := cipher.NewCTR(c, ct[:params.BlockSize]) + + m = make([]byte, len(ct)-params.BlockSize) + ctr.XORKeyStream(m, ct[params.BlockSize:]) + return +} + +// Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1. If +// the shared information parameters aren't being used, they should be +// nil. +func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) { + params := pub.Params + if params == nil { + if params = ParamsFromCurve(pub.Curve); params == nil { + err = ErrUnsupportedECIESParameters + return + } + } + R, err := GenerateKey(rand, pub.Curve, params) + if err != nil { + return + } + + hash := params.Hash() + z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen) + if err != nil { + return + } + K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + if err != nil { + return + } + Ke := K[:params.KeyLen] + Km := K[params.KeyLen:] + hash.Write(Km) + Km = hash.Sum(nil) + hash.Reset() + + em, err := symEncrypt(rand, params, Ke, m) + if err != nil || len(em) <= params.BlockSize { + return + } + + d := messageTag(params.Hash, Km, em, s2) + + Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y) + ct = make([]byte, len(Rb)+len(em)+len(d)) + copy(ct, Rb) + copy(ct[len(Rb):], em) + copy(ct[len(Rb)+len(em):], d) + return +} + +// Decrypt decrypts an ECIES ciphertext. +func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) { + if c == nil || len(c) == 0 { + err = ErrInvalidMessage + return + } + params := prv.PublicKey.Params + if params == nil { + if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil { + err = ErrUnsupportedECIESParameters + return + } + } + hash := params.Hash() + + var ( + rLen int + hLen int = hash.Size() + mStart int + mEnd int + ) + + switch c[0] { + case 2, 3, 4: + rLen = ((prv.PublicKey.Curve.Params().BitSize + 7) / 4) + if len(c) < (rLen + hLen + 1) { + err = ErrInvalidMessage + return + } + default: + err = ErrInvalidPublicKey + return + } + + mStart = rLen + mEnd = len(c) - hLen + + R := new(PublicKey) + R.Curve = prv.PublicKey.Curve + R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen]) + if R.X == nil { + err = ErrInvalidPublicKey + return + } + + z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) + if err != nil { + return + } + + K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + if err != nil { + return + } + + Ke := K[:params.KeyLen] + Km := K[params.KeyLen:] + hash.Write(Km) + Km = hash.Sum(nil) + hash.Reset() + + d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) + if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { + err = ErrInvalidMessage + return + } + + m, err = symDecrypt(rand, params, Ke, c[mStart:mEnd]) + return +} diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/ecies_test.go b/Godeps/_workspace/src/github.com/obscuren/ecies/ecies_test.go new file mode 100644 index 000000000..943e4488e --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/ecies_test.go @@ -0,0 +1,489 @@ +package ecies + +import ( + "bytes" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "flag" + "fmt" + "io/ioutil" + "testing" +) + +var dumpEnc bool + +func init() { + flDump := flag.Bool("dump", false, "write encrypted test message to file") + flag.Parse() + dumpEnc = *flDump +} + +// Ensure the KDF generates appropriately sized keys. +func TestKDF(t *testing.T) { + msg := []byte("Hello, world") + h := sha256.New() + + k, err := concatKDF(h, msg, nil, 64) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + if len(k) != 64 { + fmt.Printf("KDF: generated key is the wrong size (%d instead of 64\n", + len(k)) + t.FailNow() + } +} + +var skLen int +var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match") + +// cmpParams compares a set of ECIES parameters. We assume, as per the +// docs, that AES is the only supported symmetric encryption algorithm. +func cmpParams(p1, p2 *ECIESParams) bool { + if p1.hashAlgo != p2.hashAlgo { + return false + } else if p1.KeyLen != p2.KeyLen { + return false + } else if p1.BlockSize != p2.BlockSize { + return false + } + return true +} + +// cmpPublic returns true if the two public keys represent the same pojnt. +func cmpPublic(pub1, pub2 PublicKey) bool { + if pub1.X == nil || pub1.Y == nil { + fmt.Println(ErrInvalidPublicKey.Error()) + return false + } + if pub2.X == nil || pub2.Y == nil { + fmt.Println(ErrInvalidPublicKey.Error()) + return false + } + pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y) + pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y) + + return bytes.Equal(pub1Out, pub2Out) +} + +// cmpPrivate returns true if the two private keys are the same. +func cmpPrivate(prv1, prv2 *PrivateKey) bool { + if prv1 == nil || prv1.D == nil { + return false + } else if prv2 == nil || prv2.D == nil { + return false + } else if prv1.D.Cmp(prv2.D) != 0 { + return false + } else { + return cmpPublic(prv1.PublicKey, prv2.PublicKey) + } +} + +// Validate the ECDH component. +func TestSharedKey(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + skLen = MaxSharedKeyLength(&prv1.PublicKey) / 2 + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(sk1, sk2) { + fmt.Println(ErrBadSharedKeys.Error()) + t.FailNow() + } +} + +// Verify that the key generation code fails when too much key data is +// requested. +func TestTooBigSharedKey(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + _, err = prv1.GenerateShared(&prv2.PublicKey, skLen*2, skLen*2) + if err != ErrSharedKeyTooBig { + fmt.Println("ecdh: shared key should be too large for curve") + t.FailNow() + } + + _, err = prv2.GenerateShared(&prv1.PublicKey, skLen*2, skLen*2) + if err != ErrSharedKeyTooBig { + fmt.Println("ecdh: shared key should be too large for curve") + t.FailNow() + } +} + +// Ensure a public key can be successfully marshalled and unmarshalled, and +// that the decoded key is the same as the original. +func TestMarshalPublic(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := MarshalPublic(&prv.PublicKey) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + pub, err := UnmarshalPublic(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !cmpPublic(prv.PublicKey, *pub) { + fmt.Println("ecies: failed to unmarshal public key") + t.FailNow() + } +} + +// Ensure that a private key can be encoded into DER format, and that +// the resulting key is properly parsed back into a public key. +func TestMarshalPrivate(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := MarshalPrivate(prv) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.out", out, 0644) + } + + prv2, err := UnmarshalPrivate(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !cmpPrivate(prv, prv2) { + fmt.Println("ecdh: private key import failed") + t.FailNow() + } +} + +// Ensure that a private key can be successfully encoded to PEM format, and +// the resulting key is properly parsed back in. +func TestPrivatePEM(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := ExportPrivatePEM(prv) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.key", out, 0644) + } + + prv2, err := ImportPrivatePEM(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } else if !cmpPrivate(prv, prv2) { + fmt.Println("ecdh: import from PEM failed") + t.FailNow() + } +} + +// Ensure that a public key can be successfully encoded to PEM format, and +// the resulting key is properly parsed back in. +func TestPublicPEM(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := ExportPublicPEM(&prv.PublicKey) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.pem", out, 0644) + } + + pub2, err := ImportPublicPEM(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } else if !cmpPublic(prv.PublicKey, *pub2) { + fmt.Println("ecdh: import from PEM failed") + t.FailNow() + } +} + +// Benchmark the generation of P256 keys. +func BenchmarkGenerateKeyP256(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Benchmark the generation of P256 shared keys. +func BenchmarkGenSharedKeyP256(b *testing.B) { + prv, err := GenerateKey(rand.Reader, elliptic.P256(), nil) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + + for i := 0; i < b.N; i++ { + _, err := prv.GenerateShared(&prv.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Verify that an encrypted message can be successfully decrypted. +func TestEncryptDecrypt(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Println("ecies: plaintext doesn't match message") + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err == nil { + fmt.Println("ecies: encryption should not have succeeded") + t.FailNow() + } +} + +// TestMarshalEncryption validates the encode/decode produces a valid +// ECIES encryption key. +func TestMarshalEncryption(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := MarshalPrivate(prv1) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := UnmarshalPrivate(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Println("ecies: plaintext doesn't match message") + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + +} + +type testCase struct { + Curve elliptic.Curve + Name string + Expected bool +} + +var testCases = []testCase{ + testCase{ + Curve: elliptic.P224(), + Name: "P224", + Expected: false, + }, + testCase{ + Curve: elliptic.P256(), + Name: "P256", + Expected: true, + }, + testCase{ + Curve: elliptic.P384(), + Name: "P384", + Expected: true, + }, + testCase{ + Curve: elliptic.P521(), + Name: "P521", + Expected: true, + }, +} + +// Test parameter selection for each curve, and that P224 fails automatic +// parameter selection (see README for a discussion of P224). Ensures that +// selecting a set of parameters automatically for the given curve works. +func TestParamSelection(t *testing.T) { + for _, c := range testCases { + testParamSelection(t, c) + } +} + +func testParamSelection(t *testing.T, c testCase) { + params := ParamsFromCurve(c.Curve) + if params == nil && c.Expected { + fmt.Printf("%s (%s)\n", ErrInvalidParams.Error(), c.Name) + t.FailNow() + } else if params != nil && !c.Expected { + fmt.Printf("ecies: parameters should be invalid (%s)\n", + c.Name) + t.FailNow() + } + + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Printf("ecies: plaintext doesn't match message (%s)\n", + c.Name) + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err == nil { + fmt.Printf("ecies: encryption should not have succeeded (%s)\n", + c.Name) + t.FailNow() + } + +} + +// Ensure that the basic public key validation in the decryption operation +// works. +func TestBasicKeyValidation(t *testing.T) { + badBytes := []byte{0, 1, 5, 6, 7, 8, 9} + + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + for _, b := range badBytes { + ct[0] = b + _, err := prv.Decrypt(rand.Reader, ct, nil, nil) + if err != ErrInvalidPublicKey { + fmt.Println("ecies: validated an invalid key") + t.FailNow() + } + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/ecies/params.go b/Godeps/_workspace/src/github.com/obscuren/ecies/params.go new file mode 100644 index 000000000..b968c7c17 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/ecies/params.go @@ -0,0 +1,187 @@ +package ecies + +// This file contains parameters for ECIES encryption, specifying the +// symmetric encryption and HMAC parameters. + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + "fmt" + "hash" +) + +// The default curve for this package is the NIST P256 curve, which +// provides security equivalent to AES-128. +var DefaultCurve = elliptic.P256() + +var ( + ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") + ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") +) + +type ECIESParams struct { + Hash func() hash.Hash // hash function + hashAlgo crypto.Hash + Cipher func([]byte) (cipher.Block, error) // symmetric cipher + BlockSize int // block size of symmetric cipher + KeyLen int // length of symmetric key +} + +// Standard ECIES parameters: +// * ECIES using AES128 and HMAC-SHA-256-16 +// * ECIES using AES256 and HMAC-SHA-256-32 +// * ECIES using AES256 and HMAC-SHA-384-48 +// * ECIES using AES256 and HMAC-SHA-512-64 +var ( + ECIES_AES128_SHA256 *ECIESParams + ECIES_AES256_SHA256 *ECIESParams + ECIES_AES256_SHA384 *ECIESParams + ECIES_AES256_SHA512 *ECIESParams +) + +func init() { + ECIES_AES128_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 16, + } + + ECIES_AES256_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA384 = &ECIESParams{ + Hash: sha512.New384, + hashAlgo: crypto.SHA384, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA512 = &ECIESParams{ + Hash: sha512.New, + hashAlgo: crypto.SHA512, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } +} + +var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ + elliptic.P256(): ECIES_AES128_SHA256, + elliptic.P384(): ECIES_AES256_SHA384, + elliptic.P521(): ECIES_AES256_SHA512, +} + +func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { + paramsFromCurve[curve] = params +} + +// ParamsFromCurve selects parameters optimal for the selected elliptic curve. +// Only the curves P256, P384, and P512 are supported. +func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) { + return paramsFromCurve[curve] + + /* + switch curve { + case elliptic.P256(): + return ECIES_AES128_SHA256 + case elliptic.P384(): + return ECIES_AES256_SHA384 + case elliptic.P521(): + return ECIES_AES256_SHA512 + default: + return nil + } + */ +} + +// ASN.1 encode the ECIES parameters relevant to the encryption operations. +func paramsToASNECIES(params *ECIESParams) (asnParams asnECIESParameters) { + if nil == params { + return + } + asnParams.KDF = asnNISTConcatenationKDF + asnParams.MAC = hmacFull + switch params.KeyLen { + case 16: + asnParams.Sym = aes128CTRinECIES + case 24: + asnParams.Sym = aes192CTRinECIES + case 32: + asnParams.Sym = aes256CTRinECIES + } + return +} + +// ASN.1 encode the ECIES parameters relevant to ECDH. +func paramsToASNECDH(params *ECIESParams) (algo asnECDHAlgorithm) { + switch params.hashAlgo { + case crypto.SHA224: + algo = dhSinglePass_stdDH_sha224kdf + case crypto.SHA256: + algo = dhSinglePass_stdDH_sha256kdf + case crypto.SHA384: + algo = dhSinglePass_stdDH_sha384kdf + case crypto.SHA512: + algo = dhSinglePass_stdDH_sha512kdf + } + return +} + +// ASN.1 decode the ECIES parameters relevant to the encryption stage. +func asnECIEStoParams(asnParams asnECIESParameters, params *ECIESParams) { + if !asnParams.KDF.Cmp(asnNISTConcatenationKDF) { + params = nil + return + } else if !asnParams.MAC.Cmp(hmacFull) { + params = nil + return + } + + switch { + case asnParams.Sym.Cmp(aes128CTRinECIES): + params.KeyLen = 16 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + case asnParams.Sym.Cmp(aes192CTRinECIES): + params.KeyLen = 24 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + case asnParams.Sym.Cmp(aes256CTRinECIES): + params.KeyLen = 32 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + default: + params = nil + } +} + +// ASN.1 decode the ECIES parameters relevant to ECDH. +func asnECDHtoParams(asnParams asnECDHAlgorithm, params *ECIESParams) { + if asnParams.Cmp(dhSinglePass_stdDH_sha224kdf) { + params.hashAlgo = crypto.SHA224 + params.Hash = sha256.New224 + } else if asnParams.Cmp(dhSinglePass_stdDH_sha256kdf) { + params.hashAlgo = crypto.SHA256 + params.Hash = sha256.New + } else if asnParams.Cmp(dhSinglePass_stdDH_sha384kdf) { + params.hashAlgo = crypto.SHA384 + params.Hash = sha512.New384 + } else if asnParams.Cmp(dhSinglePass_stdDH_sha512kdf) { + params.hashAlgo = crypto.SHA512 + params.Hash = sha512.New + } else { + params = nil + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/.gitignore b/Godeps/_workspace/src/github.com/obscuren/otto/.gitignore new file mode 100644 index 000000000..8c2a16949 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/.gitignore @@ -0,0 +1,5 @@ +/.test +/otto/otto +/otto/otto-* +/test/test-*.js +/test/tester diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/DESIGN.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/DESIGN.markdown new file mode 100644 index 000000000..288752987 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/DESIGN.markdown @@ -0,0 +1 @@ +* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.) diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/LICENSE b/Godeps/_workspace/src/github.com/obscuren/otto/LICENSE new file mode 100644 index 000000000..b6179fe38 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/LICENSE @@ -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. diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/Makefile new file mode 100644 index 000000000..477723bd0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/Makefile @@ -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 --run Test\($(subst $(eval) ,\|,$(TESTS))\) +TEST := -v +TEST := . + +test: parser inline.go + go test -i + go test $(TEST) + @echo PASS + +parser: + $(MAKE) -C parser + +inline.go: inline + ./$< > $@ + +################# +# 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 $@ + diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/README.markdown new file mode 100644 index 000000000..77ad0c4ed --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/README.markdown @@ -0,0 +1,798 @@ +# otto +-- + import "github.com/robertkrimen/otto" + +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.UndefinedValue() + }) + +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. + * Error reporting needs to be improved. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + + +### 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()) + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + vm.Run(unsafe) // Here be dragons (risky code) + vm.Interrupt = nil + } + +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/ + +## Usage + +```go +var ErrVersion = errors.New("version mismatch") +``` + +#### 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. + + // 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. + + script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + vm.Run(script) + +#### func (*Otto) Copy + +```go +func (self *Otto) Copy() *Otto +``` +Copy will create a copy/clone of the runtime. + +Copy is useful for saving some processing time when creating many similar +runtimes. + +This implementation is alpha-ish, and works by introspecting every part of the +runtime and reallocating and then relinking everything back together. Please +report if you notice any inadvertent sharing of data between copies. + +#### 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: + + 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 (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: + + ToValue(false) + +#### func NaNValue + +```go +func NaNValue() Value +``` +NaNValue will return a value representing NaN. + +It is equivalent to: + + 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 probably should not use it. + +ToValue may be deprecated and removed in the near future. + +Try Otto.ToValue for a replacement. + +#### func TrueValue + +```go +func TrueValue() Value +``` +TrueValue will return a value representing true. + +It is equivalent to: + + 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. + +WARNING: The interface function will be changing soon to: + + Export() interface{} + +If a reasonable conversion is not possible, then the original result is +returned. + + undefined -> otto.Value (UndefinedValue()) + null -> interface{}(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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/array_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/array_test.go new file mode 100644 index 000000000..06f481bd4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/array_test.go @@ -0,0 +1,716 @@ +package otto + +import ( + "testing" +) + +func TestArray(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = [ undefined, "Nothing happens." ]; + abc.length; + `, 2) + + test(` + abc = ""+[0, 1, 2, 3]; + def = [].toString(); + ghi = [null, 4, "null"].toString(); + [ abc, def, ghi ]; + `, "0,1,2,3,,,4,null") + + test(`new Array(0).length`, 0) + + test(`new Array(11).length`, 11) + + test(`new Array(11, 1).length`, 2) + + test(` + abc = [0, 1, 2, 3]; + abc.xyzzy = "Nothing happens."; + delete abc[1]; + var xyzzy = delete abc.xyzzy; + [ abc, xyzzy, abc.xyzzy ]; + `, "0,,2,3,true,") + + test(` + var abc = [0, 1, 2, 3, 4]; + abc.length = 2; + abc; + `, "0,1") + + test(`raise: + [].length = 3.14159; + `, "RangeError") + + test(`raise: + new Array(3.14159); + `, "RangeError") + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + abc = [101]; + abc.hasOwnProperty("0") && abc[0] === 101; + `, true) + + test(` + abc = [,,undefined]; + [ abc.hasOwnProperty(0), abc.hasOwnProperty(1), abc.hasOwnProperty(2) ]; + `, "false,false,true") + + test(` + abc = Object.getOwnPropertyDescriptor(Array, "prototype"); + [ [ typeof Array.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestArray_toString(t *testing.T) { + tt(t, func() { + { + test(` + Array.prototype.toString = function() { + return "Nothing happens."; + } + abc = Array.prototype.toString(); + def = [].toString(); + ghi = [null, 4, "null"].toString(); + + [ abc, def, ghi ].join(","); + `, "Nothing happens.,Nothing happens.,Nothing happens.") + } + + { + test(` + Array.prototype.join = undefined + abc = Array.prototype.toString() + def = [].toString() + ghi = [null, 4, "null"].toString() + + abc + "," + def + "," + ghi; + `, "[object Array],[object Array],[object Array]") + } + }) +} + +func TestArray_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + [ 3.14159, "abc", undefined, new Date(0) ].toLocaleString(); + `, "3.14159,abc,,1970-01-01 00:00:00") + + test(`raise: + [ { toLocaleString: undefined } ].toLocaleString(); + `, "TypeError") + }) +} + +func TestArray_concat(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = [-1, -2, -3]; + ghi = abc.concat(def); + jkl = abc.concat(def, 3, 4, 5); + mno = def.concat(-4, -5, abc); + + [ ghi, jkl, mno ].join(";"); + `, "0,1,2,-1,-2,-3;0,1,2,-1,-2,-3,3,4,5;-1,-2,-3,-4,-5,0,1,2") + + test(` + var abc = [,1]; + var def = abc.concat([], [,]); + + def.getClass = Object.prototype.toString; + + [ def.getClass(), typeof def[0], def[1], typeof def[2], def.length ]; + `, "[object Array],undefined,1,undefined,3") + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + + var abc = Array.prototype.concat.call(101); + + var hasProperty = abc.hasOwnProperty("0"); + var instanceOfVerify = typeof abc[0] === "object"; + var verifyValue = false; + verifyValue = abc[0] == 101; + + var verifyEnumerable = false; + for (var property in abc) { + if (property === "0" && abc.hasOwnProperty("0")) { + verifyEnumerable = true; + } + } + + var verifyWritable = false; + abc[0] = 12; + verifyWritable = abc[0] === 12; + + var verifyConfigurable = false; + delete abc[0]; + verifyConfigurable = abc.hasOwnProperty("0"); + + [ hasProperty, instanceOfVerify, verifyValue, !verifyConfigurable, verifyEnumerable, verifyWritable ]; + `, "true,true,true,true,true,true") + }) +} + +func TestArray_splice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = abc.splice(1, 2, 3, 4, 5); + ghi = [].concat(abc); + jkl = ghi.splice(17, 21, 7, 8, 9); + [ abc, def, ghi, jkl ].join(";"); + `, "0,3,4,5;1,2;0,3,4,5,7,8,9;") + }) +} + +func TestArray_shift(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = abc.shift(); + ghi = [].concat(abc); + jkl = abc.shift(); + mno = [].concat(abc); + pqr = abc.shift(); + stu = [].concat(abc); + vwx = abc.shift(); + + [ abc, def, ghi, jkl, mno, pqr, stu, vwx ].join(";"); + `, ";0;1,2;1;2;2;;") + }) +} + +func TestArray_push(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0]; + def = abc.push(1); + ghi = [].concat(abc); + jkl = abc.push(2,3,4); + + [ abc, def, ghi, jkl ].join(";"); + `, "0,1,2,3,4;2;0,1;5") + }) +} + +func TestArray_pop(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1]; + def = abc.pop(); + ghi = [].concat(abc); + jkl = abc.pop(); + mno = [].concat(abc); + pqr = abc.pop(); + + [ abc, def, ghi, jkl, mno, pqr ].join(";"); + `, ";1;0;0;;") + }) +} + +func TestArray_slice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3]; + def = abc.slice(); + ghi = abc.slice(1); + jkl = abc.slice(3,-1); + mno = abc.slice(2,-1); + pqr = abc.slice(-1, -10); + + [ abc, def, ghi, jkl, mno, pqr ].join(";"); + `, "0,1,2,3;0,1,2,3;1,2,3;;2;") + + // Array.protoype.slice is generic + test(` + abc = { 0: 0, 1: 1, 2: 2, 3: 3 }; + abc.length = 4; + def = Array.prototype.slice.call(abc); + ghi = Array.prototype.slice.call(abc,1); + jkl = Array.prototype.slice.call(abc,3,-1); + mno = Array.prototype.slice.call(abc,2,-1); + pqr = Array.prototype.slice.call(abc,-1,-10); + + [ abc, def, ghi, jkl, pqr ].join(";"); + `, "[object Object];0,1,2,3;1,2,3;;") + }) +} + +func TestArray_sliceArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(){ + return Array.prototype.slice.call(arguments, 1) + })({}, 1, 2, 3); + `, "1,2,3") + }) +} + +func TestArray_unshift(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = []; + def = abc.unshift(0); + ghi = [].concat(abc); + jkl = abc.unshift(1,2,3,4); + + [ abc, def, ghi, jkl ].join(";"); + `, "1,2,3,4,0;1;0;5") + }) +} + +func TestArray_reverse(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3].reverse(); + def = [0,1,2].reverse(); + + [ abc, def ]; + `, "3,2,1,0,2,1,0") + }) +} + +func TestArray_sort(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3].sort(); + def = [3,2,1,0].sort(); + ghi = [].sort(); + jkl = [0].sort(); + mno = [1,0].sort(); + pqr = [1,5,-10, 100, 8, 72, 401, 0.05].sort(); + stu = [1,5,-10, 100, 8, 72, 401, 0.05].sort(function(x, y){ + return x == y ? 0 : x < y ? -1 : 1 + }); + + [ abc, def, ghi, jkl, mno, pqr, stu ].join(";"); + `, "0,1,2,3;0,1,2,3;;0;0,1;-10,0.05,1,100,401,5,72,8;-10,0.05,1,5,8,72,100,401") + + test(`Array.prototype.sort.length`, 1) + }) +} + +func TestArray_isArray(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ Array.isArray.length, Array.isArray(), Array.isArray([]), Array.isArray({}) ]; + `, "1,false,true,false") + + test(`Array.isArray(Math)`, false) + }) +} + +func TestArray_indexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`['a', 'b', 'c', 'b'].indexOf('b')`, 1) + + test(`['a', 'b', 'c', 'b'].indexOf('b', 2)`, 3) + + test(`['a', 'b', 'c', 'b'].indexOf('b', -2)`, 3) + + test(` + Object.prototype.indexOf = Array.prototype.indexOf; + var abc = {0: 'a', 1: 'b', 2: 'c', length: 3}; + abc.indexOf('c'); + `, 2) + + test(`[true].indexOf(true, "-Infinity")`, 0) + + test(` + var target = {}; + Math[3] = target; + Math.length = 5; + Array.prototype.indexOf.call(Math, target) === 3; + `, true) + + test(` + var _NaN = NaN; + var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); + abc.indexOf(NaN); + `, -1) + + test(` + var abc = {toString:function (){return 0}}; + var def = 1; + var ghi = -(4/3); + var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); + [ jkl.indexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; + `, "14,7,7,10") + }) +} + +func TestArray_lastIndexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b')`, 3) + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b', 2)`, 1) + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b', -2)`, 1) + + test(` + Object.prototype.lastIndexOf = Array.prototype.lastIndexOf; + var abc = {0: 'a', 1: 'b', 2: 'c', 3: 'b', length: 4}; + abc.lastIndexOf('b'); + `, 3) + + test(` + var target = {}; + Math[3] = target; + Math.length = 5; + [ Array.prototype.lastIndexOf.call(Math, target) === 3 ]; + `, "true") + + test(` + var _NaN = NaN; + var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); + abc.lastIndexOf(NaN); + `, -1) + + test(` + var abc = {toString:function (){return 0}}; + var def = 1; + var ghi = -(4/3); + var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); + [ jkl.lastIndexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; + `, "15,7,7,10") + }) +} + +func TestArray_every(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].every()`, "TypeError") + + test(`raise: [].every("abc")`, "TypeError") + + test(`[].every(function() { return false })`, true) + + test(`[1,2,3].every(function() { return false })`, false) + + test(`[1,2,3].every(function() { return true })`, true) + + test(`[1,2,3].every(function(_, index) { if (index === 1) return true })`, false) + + test(` + var abc = function(value, index, object) { + return ('[object Math]' !== Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + !Array.prototype.every.call(Math, abc); + `, true) + + test(` + var def = false; + + var abc = function(value, index, object) { + def = true; + return this === Math; + }; + + [11].every(abc, Math) && def; + `, true) + + test(` + var def = false; + + var abc = function(value, index, object) { + def = true; + return Math; + }; + + [11].every(abc) && def; + `, true) + }) +} + +func TestArray_some(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].some("abc")`, "TypeError") + + test(`[].some(function() { return true })`, false) + + test(`[1,2,3].some(function() { return false })`, false) + + test(`[1,2,3].some(function() { return true })`, true) + + test(`[1,2,3].some(function(_, index) { if (index === 1) return true })`, true) + + test(` + var abc = function(value, index, object) { + return ('[object Math]' !== Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + !Array.prototype.some.call(Math, abc); + `, true) + + test(` + var abc = function(value, index, object) { + return this === Math; + }; + + [11].some(abc, Math); + `, true) + + test(` + var abc = function(value, index, object) { + return Math; + }; + + [11].some(abc); + `, true) + }) +} + +func TestArray_forEach(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].forEach("abc")`, "TypeError") + + test(` + var abc = 0; + [].forEach(function(value) { + abc += value; + }); + abc; + `, 0) + + test(` + abc = 0; + var def = []; + [1,2,3].forEach(function(value, index) { + abc += value; + def.push(index); + }); + [ abc, def ]; + `, "6,0,1,2") + + test(` + var def = false; + var abc = function(value, index, object) { + def = ('[object Math]' === Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + Array.prototype.forEach.call(Math, abc); + def; + `, true) + + test(` + var def = false; + var abc = function(value, index, object) { + def = this === Math; + }; + + [11].forEach(abc, Math); + def; + `, true) + }) +} + +func TestArray_indexing(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = new Array(0, 1); + var def = abc.length; + abc[4294967296] = 10; // 2^32 => 0 + abc[4294967297] = 11; // 2^32+1 => 1 + [ def, abc.length, abc[0], abc[1], abc[4294967296] ]; + `, "2,2,0,1,10") + + test(` + abc = new Array(0, 1); + def = abc.length; + abc[4294967295] = 10; + var ghi = abc.length; + abc[4294967299] = 12; + var jkl = abc.length; + abc[4294967294] = 11; + [ def, ghi, jkl, abc.length, abc[4294967295], abc[4294967299] ]; + `, "2,2,2,4294967295,10,12") + }) +} + +func TestArray_map(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].map("abc")`, "TypeError") + + test(`[].map(function() { return 1 }).length`, 0) + + test(`[1,2,3].map(function(value) { return value * value })`, "1,4,9") + + test(`[1,2,3].map(function(value) { return 1 })`, "1,1,1") + + test(` + var abc = function(value, index, object) { + return ('[object Math]' === Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + Array.prototype.map.call(Math, abc)[0]; + `, true) + + test(` + var abc = function(value, index, object) { + return this === Math; + }; + + [11].map(abc, Math)[0]; + `, true) + }) +} + +func TestArray_filter(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].filter("abc")`, "TypeError") + + test(`[].filter(function() { return 1 }).length`, 0) + + test(`[1,2,3].filter(function() { return false }).length`, 0) + + test(`[1,2,3].filter(function() { return true })`, "1,2,3") + }) +} + +func TestArray_reduce(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].reduce("abc")`, "TypeError") + + test(`raise: [].reduce(function() {})`, "TypeError") + + test(`[].reduce(function() {}, 0)`, 0) + + test(`[].reduce(function() {}, undefined)`, "undefined") + + test(`['a','b','c'].reduce(function(result, value) { return result+', '+value })`, "a, b, c") + + test(`[1,2,3].reduce(function(result, value) { return result + value }, 4)`, 10) + + test(`[1,2,3].reduce(function(result, value) { return result + value })`, 6) + }) +} + +func TestArray_reduceRight(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].reduceRight("abc")`, "TypeError") + + test(`raise: [].reduceRight(function() {})`, "TypeError") + + test(`[].reduceRight(function() {}, 0)`, 0) + + test(`[].reduceRight(function() {}, undefined)`, "undefined") + + test(`['a','b','c'].reduceRight(function(result, value) { return result+', '+value })`, "c, b, a") + + test(`[1,2,3].reduceRight(function(result, value) { return result + value }, 4)`, 10) + + test(`[1,2,3].reduceRight(function(result, value) { return result + value })`, 6) + }) +} + +func TestArray_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = []; + Object.defineProperty(abc, "length", { + writable: false + }); + abc.length; + `, 0) + + test(`raise: + var abc = []; + var exception; + Object.defineProperty(abc, "length", { + writable: false + }); + Object.defineProperty(abc, "length", { + writable: true + }); + `, "TypeError") + }) +} + +func TestArray_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = new Array(null); + var def = new Array(undefined); + [ abc.length, abc[0] === null, def.length, def[0] === undefined ] + `, "1,true,1,true") + + test(` + var abc = new Array(new Number(0)); + var def = new Array(new Number(4294967295)); + [ abc.length, typeof abc[0], abc[0] == 0, def.length, typeof def[0], def[0] == 4294967295 ] + `, "1,object,true,1,object,true") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/ast/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/ast/README.markdown new file mode 100644 index 000000000..a12e048d5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/ast/README.markdown @@ -0,0 +1,1066 @@ +# ast +-- + import "github.com/robertkrimen/otto/ast" + +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. + +## Usage + +#### type ArrayLiteral + +```go +type ArrayLiteral struct { + LeftBracket file.Idx + RightBracket file.Idx + Value []Expression +} +``` + + +#### func (*ArrayLiteral) Idx0 + +```go +func (self *ArrayLiteral) Idx0() file.Idx +``` + +#### func (*ArrayLiteral) Idx1 + +```go +func (self *ArrayLiteral) Idx1() file.Idx +``` + +#### type AssignExpression + +```go +type AssignExpression struct { + Operator token.Token + Left Expression + Right Expression +} +``` + + +#### func (*AssignExpression) Idx0 + +```go +func (self *AssignExpression) Idx0() file.Idx +``` + +#### func (*AssignExpression) Idx1 + +```go +func (self *AssignExpression) Idx1() file.Idx +``` + +#### type BadExpression + +```go +type BadExpression struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadExpression) Idx0 + +```go +func (self *BadExpression) Idx0() file.Idx +``` + +#### func (*BadExpression) Idx1 + +```go +func (self *BadExpression) Idx1() file.Idx +``` + +#### type BadStatement + +```go +type BadStatement struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadStatement) Idx0 + +```go +func (self *BadStatement) Idx0() file.Idx +``` + +#### func (*BadStatement) Idx1 + +```go +func (self *BadStatement) Idx1() file.Idx +``` + +#### type BinaryExpression + +```go +type BinaryExpression struct { + Operator token.Token + Left Expression + Right Expression + Comparison bool +} +``` + + +#### func (*BinaryExpression) Idx0 + +```go +func (self *BinaryExpression) Idx0() file.Idx +``` + +#### func (*BinaryExpression) Idx1 + +```go +func (self *BinaryExpression) Idx1() file.Idx +``` + +#### type BlockStatement + +```go +type BlockStatement struct { + LeftBrace file.Idx + List []Statement + RightBrace file.Idx +} +``` + + +#### func (*BlockStatement) Idx0 + +```go +func (self *BlockStatement) Idx0() file.Idx +``` + +#### func (*BlockStatement) Idx1 + +```go +func (self *BlockStatement) Idx1() file.Idx +``` + +#### type BooleanLiteral + +```go +type BooleanLiteral struct { + Idx file.Idx + Literal string + Value bool +} +``` + + +#### func (*BooleanLiteral) Idx0 + +```go +func (self *BooleanLiteral) Idx0() file.Idx +``` + +#### func (*BooleanLiteral) Idx1 + +```go +func (self *BooleanLiteral) Idx1() file.Idx +``` + +#### type BracketExpression + +```go +type BracketExpression struct { + Left Expression + Member Expression + LeftBracket file.Idx + RightBracket file.Idx +} +``` + + +#### func (*BracketExpression) Idx0 + +```go +func (self *BracketExpression) Idx0() file.Idx +``` + +#### func (*BracketExpression) Idx1 + +```go +func (self *BracketExpression) Idx1() file.Idx +``` + +#### type BranchStatement + +```go +type BranchStatement struct { + Idx file.Idx + Token token.Token + Label *Identifier +} +``` + + +#### func (*BranchStatement) Idx0 + +```go +func (self *BranchStatement) Idx0() file.Idx +``` + +#### func (*BranchStatement) Idx1 + +```go +func (self *BranchStatement) Idx1() file.Idx +``` + +#### type CallExpression + +```go +type CallExpression struct { + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*CallExpression) Idx0 + +```go +func (self *CallExpression) Idx0() file.Idx +``` + +#### func (*CallExpression) Idx1 + +```go +func (self *CallExpression) Idx1() file.Idx +``` + +#### type CaseStatement + +```go +type CaseStatement struct { + Case file.Idx + Test Expression + Consequent []Statement +} +``` + + +#### func (*CaseStatement) Idx0 + +```go +func (self *CaseStatement) Idx0() file.Idx +``` + +#### func (*CaseStatement) Idx1 + +```go +func (self *CaseStatement) Idx1() file.Idx +``` + +#### type CatchStatement + +```go +type CatchStatement struct { + Catch file.Idx + Parameter *Identifier + Body Statement +} +``` + + +#### func (*CatchStatement) Idx0 + +```go +func (self *CatchStatement) Idx0() file.Idx +``` + +#### func (*CatchStatement) Idx1 + +```go +func (self *CatchStatement) Idx1() file.Idx +``` + +#### type ConditionalExpression + +```go +type ConditionalExpression struct { + Test Expression + Consequent Expression + Alternate Expression +} +``` + + +#### func (*ConditionalExpression) Idx0 + +```go +func (self *ConditionalExpression) Idx0() file.Idx +``` + +#### func (*ConditionalExpression) Idx1 + +```go +func (self *ConditionalExpression) Idx1() file.Idx +``` + +#### type DebuggerStatement + +```go +type DebuggerStatement struct { + Debugger file.Idx +} +``` + + +#### func (*DebuggerStatement) Idx0 + +```go +func (self *DebuggerStatement) Idx0() file.Idx +``` + +#### func (*DebuggerStatement) Idx1 + +```go +func (self *DebuggerStatement) Idx1() file.Idx +``` + +#### type Declaration + +```go +type Declaration interface { + // contains filtered or unexported methods +} +``` + +All declaration nodes implement the Declaration interface. + +#### type DoWhileStatement + +```go +type DoWhileStatement struct { + Do file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*DoWhileStatement) Idx0 + +```go +func (self *DoWhileStatement) Idx0() file.Idx +``` + +#### func (*DoWhileStatement) Idx1 + +```go +func (self *DoWhileStatement) Idx1() file.Idx +``` + +#### type DotExpression + +```go +type DotExpression struct { + Left Expression + Identifier Identifier +} +``` + + +#### func (*DotExpression) Idx0 + +```go +func (self *DotExpression) Idx0() file.Idx +``` + +#### func (*DotExpression) Idx1 + +```go +func (self *DotExpression) Idx1() file.Idx +``` + +#### type EmptyStatement + +```go +type EmptyStatement struct { + Semicolon file.Idx +} +``` + + +#### func (*EmptyStatement) Idx0 + +```go +func (self *EmptyStatement) Idx0() file.Idx +``` + +#### func (*EmptyStatement) Idx1 + +```go +func (self *EmptyStatement) Idx1() file.Idx +``` + +#### type Expression + +```go +type Expression interface { + Node + // contains filtered or unexported methods +} +``` + +All expression nodes implement the Expression interface. + +#### type ExpressionStatement + +```go +type ExpressionStatement struct { + Expression Expression +} +``` + + +#### func (*ExpressionStatement) Idx0 + +```go +func (self *ExpressionStatement) Idx0() file.Idx +``` + +#### func (*ExpressionStatement) Idx1 + +```go +func (self *ExpressionStatement) Idx1() file.Idx +``` + +#### type ForInStatement + +```go +type ForInStatement struct { + For file.Idx + Into Expression + Source Expression + Body Statement +} +``` + + +#### func (*ForInStatement) Idx0 + +```go +func (self *ForInStatement) Idx0() file.Idx +``` + +#### func (*ForInStatement) Idx1 + +```go +func (self *ForInStatement) Idx1() file.Idx +``` + +#### type ForStatement + +```go +type ForStatement struct { + For file.Idx + Initializer Expression + Update Expression + Test Expression + Body Statement +} +``` + + +#### func (*ForStatement) Idx0 + +```go +func (self *ForStatement) Idx0() file.Idx +``` + +#### func (*ForStatement) Idx1 + +```go +func (self *ForStatement) Idx1() file.Idx +``` + +#### type FunctionDeclaration + +```go +type FunctionDeclaration struct { + Function *FunctionLiteral +} +``` + + +#### type FunctionLiteral + +```go +type FunctionLiteral struct { + Function file.Idx + Name *Identifier + ParameterList *ParameterList + Body Statement + Source string + + DeclarationList []Declaration +} +``` + + +#### func (*FunctionLiteral) Idx0 + +```go +func (self *FunctionLiteral) Idx0() file.Idx +``` + +#### func (*FunctionLiteral) Idx1 + +```go +func (self *FunctionLiteral) Idx1() file.Idx +``` + +#### type Identifier + +```go +type Identifier struct { + Name string + Idx file.Idx +} +``` + + +#### func (*Identifier) Idx0 + +```go +func (self *Identifier) Idx0() file.Idx +``` + +#### func (*Identifier) Idx1 + +```go +func (self *Identifier) Idx1() file.Idx +``` + +#### type IfStatement + +```go +type IfStatement struct { + If file.Idx + Test Expression + Consequent Statement + Alternate Statement +} +``` + + +#### func (*IfStatement) Idx0 + +```go +func (self *IfStatement) Idx0() file.Idx +``` + +#### func (*IfStatement) Idx1 + +```go +func (self *IfStatement) Idx1() file.Idx +``` + +#### type LabelledStatement + +```go +type LabelledStatement struct { + Label *Identifier + Colon file.Idx + Statement Statement +} +``` + + +#### func (*LabelledStatement) Idx0 + +```go +func (self *LabelledStatement) Idx0() file.Idx +``` + +#### func (*LabelledStatement) Idx1 + +```go +func (self *LabelledStatement) Idx1() file.Idx +``` + +#### type NewExpression + +```go +type NewExpression struct { + New file.Idx + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*NewExpression) Idx0 + +```go +func (self *NewExpression) Idx0() file.Idx +``` + +#### func (*NewExpression) Idx1 + +```go +func (self *NewExpression) Idx1() file.Idx +``` + +#### type Node + +```go +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 +} +``` + +All nodes implement the Node interface. + +#### type NullLiteral + +```go +type NullLiteral struct { + Idx file.Idx + Literal string +} +``` + + +#### func (*NullLiteral) Idx0 + +```go +func (self *NullLiteral) Idx0() file.Idx +``` + +#### func (*NullLiteral) Idx1 + +```go +func (self *NullLiteral) Idx1() file.Idx +``` + +#### type NumberLiteral + +```go +type NumberLiteral struct { + Idx file.Idx + Literal string + Value interface{} +} +``` + + +#### func (*NumberLiteral) Idx0 + +```go +func (self *NumberLiteral) Idx0() file.Idx +``` + +#### func (*NumberLiteral) Idx1 + +```go +func (self *NumberLiteral) Idx1() file.Idx +``` + +#### type ObjectLiteral + +```go +type ObjectLiteral struct { + LeftBrace file.Idx + RightBrace file.Idx + Value []Property +} +``` + + +#### func (*ObjectLiteral) Idx0 + +```go +func (self *ObjectLiteral) Idx0() file.Idx +``` + +#### func (*ObjectLiteral) Idx1 + +```go +func (self *ObjectLiteral) Idx1() file.Idx +``` + +#### type ParameterList + +```go +type ParameterList struct { + Opening file.Idx + List []*Identifier + Closing file.Idx +} +``` + + +#### type Program + +```go +type Program struct { + Body []Statement + + DeclarationList []Declaration +} +``` + + +#### func (*Program) Idx0 + +```go +func (self *Program) Idx0() file.Idx +``` + +#### func (*Program) Idx1 + +```go +func (self *Program) Idx1() file.Idx +``` + +#### type Property + +```go +type Property struct { + Key string + Kind string + Value Expression +} +``` + + +#### type RegExpLiteral + +```go +type RegExpLiteral struct { + Idx file.Idx + Literal string + Pattern string + Flags string + Value string +} +``` + + +#### func (*RegExpLiteral) Idx0 + +```go +func (self *RegExpLiteral) Idx0() file.Idx +``` + +#### func (*RegExpLiteral) Idx1 + +```go +func (self *RegExpLiteral) Idx1() file.Idx +``` + +#### type ReturnStatement + +```go +type ReturnStatement struct { + Return file.Idx + Argument Expression +} +``` + + +#### func (*ReturnStatement) Idx0 + +```go +func (self *ReturnStatement) Idx0() file.Idx +``` + +#### func (*ReturnStatement) Idx1 + +```go +func (self *ReturnStatement) Idx1() file.Idx +``` + +#### type SequenceExpression + +```go +type SequenceExpression struct { + Sequence []Expression +} +``` + + +#### func (*SequenceExpression) Idx0 + +```go +func (self *SequenceExpression) Idx0() file.Idx +``` + +#### func (*SequenceExpression) Idx1 + +```go +func (self *SequenceExpression) Idx1() file.Idx +``` + +#### type Statement + +```go +type Statement interface { + Node + // contains filtered or unexported methods +} +``` + +All statement nodes implement the Statement interface. + +#### type StringLiteral + +```go +type StringLiteral struct { + Idx file.Idx + Literal string + Value string +} +``` + + +#### func (*StringLiteral) Idx0 + +```go +func (self *StringLiteral) Idx0() file.Idx +``` + +#### func (*StringLiteral) Idx1 + +```go +func (self *StringLiteral) Idx1() file.Idx +``` + +#### type SwitchStatement + +```go +type SwitchStatement struct { + Switch file.Idx + Discriminant Expression + Default int + Body []*CaseStatement +} +``` + + +#### func (*SwitchStatement) Idx0 + +```go +func (self *SwitchStatement) Idx0() file.Idx +``` + +#### func (*SwitchStatement) Idx1 + +```go +func (self *SwitchStatement) Idx1() file.Idx +``` + +#### type ThisExpression + +```go +type ThisExpression struct { + Idx file.Idx +} +``` + + +#### func (*ThisExpression) Idx0 + +```go +func (self *ThisExpression) Idx0() file.Idx +``` + +#### func (*ThisExpression) Idx1 + +```go +func (self *ThisExpression) Idx1() file.Idx +``` + +#### type ThrowStatement + +```go +type ThrowStatement struct { + Throw file.Idx + Argument Expression +} +``` + + +#### func (*ThrowStatement) Idx0 + +```go +func (self *ThrowStatement) Idx0() file.Idx +``` + +#### func (*ThrowStatement) Idx1 + +```go +func (self *ThrowStatement) Idx1() file.Idx +``` + +#### type TryStatement + +```go +type TryStatement struct { + Try file.Idx + Body Statement + Catch *CatchStatement + Finally Statement +} +``` + + +#### func (*TryStatement) Idx0 + +```go +func (self *TryStatement) Idx0() file.Idx +``` + +#### func (*TryStatement) Idx1 + +```go +func (self *TryStatement) Idx1() file.Idx +``` + +#### type UnaryExpression + +```go +type UnaryExpression struct { + Operator token.Token + Idx file.Idx // If a prefix operation + Operand Expression + Postfix bool +} +``` + + +#### func (*UnaryExpression) Idx0 + +```go +func (self *UnaryExpression) Idx0() file.Idx +``` + +#### func (*UnaryExpression) Idx1 + +```go +func (self *UnaryExpression) Idx1() file.Idx +``` + +#### type VariableDeclaration + +```go +type VariableDeclaration struct { + Var file.Idx + List []*VariableExpression +} +``` + + +#### type VariableExpression + +```go +type VariableExpression struct { + Name string + Idx file.Idx + Initializer Expression +} +``` + + +#### func (*VariableExpression) Idx0 + +```go +func (self *VariableExpression) Idx0() file.Idx +``` + +#### func (*VariableExpression) Idx1 + +```go +func (self *VariableExpression) Idx1() file.Idx +``` + +#### type VariableStatement + +```go +type VariableStatement struct { + Var file.Idx + List []Expression +} +``` + + +#### func (*VariableStatement) Idx0 + +```go +func (self *VariableStatement) Idx0() file.Idx +``` + +#### func (*VariableStatement) Idx1 + +```go +func (self *VariableStatement) Idx1() file.Idx +``` + +#### type WhileStatement + +```go +type WhileStatement struct { + While file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*WhileStatement) Idx0 + +```go +func (self *WhileStatement) Idx0() file.Idx +``` + +#### func (*WhileStatement) Idx1 + +```go +func (self *WhileStatement) Idx1() file.Idx +``` + +#### type WithStatement + +```go +type WithStatement struct { + With file.Idx + Object Expression + Body Statement +} +``` + + +#### func (*WithStatement) Idx0 + +```go +func (self *WithStatement) Idx0() file.Idx +``` + +#### func (*WithStatement) Idx1 + +```go +func (self *WithStatement) Idx1() file.Idx +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/ast/node.go b/Godeps/_workspace/src/github.com/obscuren/otto/ast/node.go new file mode 100644 index 000000000..c8b3cb04e --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/ast/node.go @@ -0,0 +1,496 @@ +/* +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 + } + + 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 (*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 + } + + 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 (*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 +} + +// ==== // +// 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 *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 *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 *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 } +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 *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() } diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/bug_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/bug_test.go new file mode 100644 index 000000000..0ee484ddd --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/bug_test.go @@ -0,0 +1,504 @@ +package otto + +import ( + "testing" + "time" +) + +func Test_262(t *testing.T) { + tt(t, func() { + test, _ := test() + + // 11.13.1-1-1 + test(`raise: + eval("42 = 42;"); + `, "ReferenceError: Invalid left-hand side in assignment") + }) +} + +func Test_issue5(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`'abc' === 'def'`, false) + test(`'\t' === '\r'`, false) + }) +} + +func Test_issue13(t *testing.T) { + tt(t, func() { + test, tester := test() + vm := tester.vm + + value, err := vm.ToValue(map[string]interface{}{ + "string": "Xyzzy", + "number": 42, + "array": []string{"def", "ghi"}, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + fn, err := vm.Object(` + (function(value){ + return ""+[value.string, value.number, value.array] + }) + `) + if err != nil { + t.Error(err) + t.FailNow() + } + + result, err := fn.Value().Call(fn.Value(), value) + if err != nil { + t.Error(err) + t.FailNow() + } + is(result.toString(), "Xyzzy,42,def,ghi") + + anything := struct { + Abc interface{} + }{ + Abc: map[string]interface{}{ + "def": []interface{}{ + []interface{}{ + "a", "b", "c", "", "d", "e", + }, + map[string]interface{}{ + "jkl": "Nothing happens.", + }, + }, + "ghi": -1, + }, + } + + vm.Set("anything", anything) + test(` + [ + anything, + "~", + anything.Abc, + "~", + anything.Abc.def, + "~", + anything.Abc.def[1].jkl, + "~", + anything.Abc.ghi, + ]; + `, "[object Object],~,[object Object],~,a,b,c,,d,e,[object Object],~,Nothing happens.,~,-1") + }) +} + +func Test_issue16(t *testing.T) { + tt(t, func() { + test, vm := test() + + test(` + var def = { + "abc": ["abc"], + "xyz": ["xyz"] + }; + def.abc.concat(def.xyz); + `, "abc,xyz") + + vm.Set("ghi", []string{"jkl", "mno"}) + + test(` + def.abc.concat(def.xyz).concat(ghi); + `, "abc,xyz,jkl,mno") + + test(` + ghi.concat(def.abc.concat(def.xyz)); + `, "jkl,mno,abc,xyz") + + vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) + + test(` + pqr.concat(ghi, def.abc, def, def.xyz); + `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz") + + test(` + pqr.concat(ghi, def.abc, def, def.xyz).length; + `, 9) + }) +} + +func Test_issue21(t *testing.T) { + tt(t, func() { + vm1 := New() + vm1.Run(` + abc = {} + abc.ghi = "Nothing happens."; + var jkl = 0; + abc.def = function() { + jkl += 1; + return 1; + } + `) + abc, err := vm1.Get("abc") + is(err, nil) + + vm2 := New() + vm2.Set("cba", abc) + _, err = vm2.Run(` + var pqr = 0; + cba.mno = function() { + pqr -= 1; + return 1; + } + cba.def(); + cba.def(); + cba.def(); + `) + is(err, nil) + + jkl, err := vm1.Get("jkl") + is(err, nil) + is(jkl, 3) + + _, err = vm1.Run(` + abc.mno(); + abc.mno(); + abc.mno(); + `) + is(err, nil) + + pqr, err := vm2.Get("pqr") + is(err, nil) + is(pqr, -3) + }) +} + +func Test_issue24(t *testing.T) { + tt(t, func() { + _, vm := test() + + { + vm.Set("abc", []string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.([]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", [...]string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.([3]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", &[...]string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(*[3]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", map[int]string{0: "abc", 1: "def", 2: "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(map[int]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", testStruct{Abc: true, Ghi: "Nothing happens."}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(testStruct) + is(valid, true) + + is(value.Abc, true) + is(value.Ghi, "Nothing happens.") + } + } + + { + vm.Set("abc", &testStruct{Abc: true, Ghi: "Nothing happens."}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(*testStruct) + is(valid, true) + + is(value.Abc, true) + is(value.Ghi, "Nothing happens.") + } + } + }) +} + +func Test_issue39(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0, def = [], ghi = function() { + if (abc < 10) return ++abc; + return undefined; + } + for (var jkl; (jkl = ghi());) def.push(jkl); + def; + `, "1,2,3,4,5,6,7,8,9,10") + + test(` + var abc = ["1", "2", "3", "4"]; + var def = []; + for (var ghi; (ghi = abc.shift());) { + def.push(ghi); + } + def; + `, "1,2,3,4") + }) +} + +func Test_issue64(t *testing.T) { + tt(t, func() { + test, vm := test() + + defer mockTimeLocal(time.UTC)() + + abc := map[string]interface{}{ + "time": time.Unix(0, 0), + } + vm.Set("abc", abc) + + def := struct { + Public string + private string + }{ + "Public", "private", + } + vm.Set("def", def) + + test(`"sec" in abc.time`, false) + + test(` + [ "Public" in def, "private" in def, def.Public, def.private ]; + `, "true,false,Public,") + + test(`JSON.stringify(abc)`, `{"time":"1970-01-01T00:00:00Z"}`) + }) +} + +func Test_7_3_1(t *testing.T) { + tt(t, func() { + test(` + + eval("var test7_3_1\u2028abc = 66;"); + [ abc, typeof test7_3_1 ]; + `, "66,undefined") + }) +} + +func Test_7_3_3(t *testing.T) { + tt(t, func() { + test(`raise: + eval("//\u2028 =;"); + `, "SyntaxError: Unexpected token =") + }) +} + +func Test_S7_3_A2_1_T1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + eval("'\u000Astr\u000Aing\u000A'") + `, "SyntaxError: Unexpected token ILLEGAL") + }) +} + +func Test_S7_8_3_A2_1_T1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ .0 === 0.0, .0, .1 === 0.1, .1 ] + `, "true,0,true,0.1") + }) +} + +func Test_S7_8_4_A4_2_T3(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + "\a" + `, "a") + }) +} + +func Test_S7_9_A1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var def; + abc: for (var i = 0; i <= 0; i++) { + for (var j = 0; j <= 1; j++) { + if (j === 0) { + continue abc; + } else { + def = true; + } + } + } + [ def, i, j ]; + `, ",1,0") + }) +} + +func Test_S7_9_A3(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(){ + return + 1; + })() + `, "undefined") + }) +} + +func Test_7_3_10(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + eval("var \u0061\u0062\u0063 = 3.14159;"); + abc; + `, 3.14159) + + test(` + abc = undefined; + eval("var \\u0061\\u0062\\u0063 = 3.14159;"); + abc; + `, 3.14159) + }) +} + +func Test_bug(t *testing.T) { + tt(t, func() { + test, _ := test() + + // 10.4.2-1-5 + test(` + "abc\ +def" + `, "abcdef") + + test(` + eval("'abc';\ + 'def'") + `, "def") + + // S12.6.1_A10 + test(` + var abc = 0; + do { + if(typeof(def) === "function"){ + abc = -1; + break; + } else { + abc = 1; + break; + } + } while(function def(){}); + abc; + `, 1) + + // S12.7_A7 + test(`raise: + abc: + while (true) { + eval("continue abc"); + } + `, "SyntaxError: Undefined label 'abc'") + + // S15.1.2.1_A3.3_T3 + test(`raise: + eval("return"); + `, "SyntaxError: Illegal return statement") + + // 15.2.3.3-2-33 + test(` + var abc = { "AB\n\\cd": 1 }; + Object.getOwnPropertyDescriptor(abc, "AB\n\\cd").value; + `, 1) + + // S15.3_A2_T1 + test(`raise: + Function.call(this, "var x / = 1;"); + `, "SyntaxError: Unexpected token /") + + // ? + test(` + (function(){ + var abc = []; + (function(){ + abc.push(0); + abc.push(1); + })(undefined); + if ((function(){ return true; })()) { + (function(){ + abc.push(2); + })(); + } + return abc; + })(); + `, "0,1,2") + + if false { + // 15.9.5.43-0-10 + // Should be an invalid date + test(` + date = new Date(1970, 0, -99999999, 0, 0, 0, 1); + `, "") + } + + // S7.8.3_A1.2_T1 + test(` + [ 0e1, 1e1, 2e1, 3e1, 4e1, 5e1, 6e1, 7e1, 8e1, 9e1 ]; + `, "0,10,20,30,40,50,60,70,80,90") + + // S15.10.2.7_A3_T2 + test(` + var abc = /\s+abc\s+/.exec("\t abc def"); + [ abc.length, abc.index, abc.input, abc ]; + `, "1,0,\t abc def,\t abc ") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin.go new file mode 100644 index 000000000..b4cdf9d21 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin.go @@ -0,0 +1,393 @@ +package otto + +import ( + "encoding/hex" + "fmt" + "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(toString(src)) + if call.evalHint { + runtime.EnterEvalExecutionContext(call) + defer runtime.LeaveExecutionContext() + } + returnValue := runtime.cmpl_evaluate_nodeProgram(program) + if returnValue.isEmpty() { + return UndefinedValue() + } + return returnValue +} + +func builtinGlobal_isNaN(call FunctionCall) Value { + value := toFloat(call.Argument(0)) + return toValue_bool(math.IsNaN(value)) +} + +func builtinGlobal_isFinite(call FunctionCall) Value { + value := toFloat(call.Argument(0)) + 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.TrimSpace(toString(call.Argument(0))) + 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.TrimSpace(toString(call.Argument(0))) + if parseFloat_matchBadSpecial.MatchString(input) { + return NaNValue() + } + value, err := strconv.ParseFloat(input, 64) + if err != nil { + for end := len(input); end > 0; end -= 1 { + 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(toString(value))) + } + 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(newURIError("URI malformed")) + } + if value >= 0xD800 && value <= 0xDBFF { + index += 1 + if index >= length { + panic(newURIError("URI malformed")) + } + // input = ..., value, value1, ... + value = value + value1 := input[index] + if value1 < 0xDC00 || value1 > 0xDFFF { + panic(newURIError("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(toString(call.Argument(0)), true) + if err { + panic(newURIError("URI malformed")) + } + return toValue_string(output) +} + +func builtinGlobal_decodeURIComponent(call FunctionCall) Value { + output, err := _decodeURI(toString(call.Argument(0)), false) + if err { + panic(newURIError("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(toString(call.Argument(0)))) +} + +func builtinGlobal_unescape(call FunctionCall) Value { + return toValue_string(builtin_unescape(toString(call.Argument(0)))) +} + +// Error + +func builtinError(call FunctionCall) Value { + return toValue_object(call.runtime.newError("", call.Argument(0))) +} + +func builtinNewError(self *_object, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newError("", valueOfArrayIndex(argumentList, 0))) +} + +func builtinError_toString(call FunctionCall) Value { + thisObject := call.thisObject() + if thisObject == nil { + panic(newTypeError()) + } + + name := "Error" + nameValue := thisObject.get("name") + if nameValue.IsDefined() { + name = toString(nameValue) + } + + message := "" + messageValue := thisObject.get("message") + if messageValue.IsDefined() { + message = toString(messageValue) + } + + 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)) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_array.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_array.go new file mode 100644 index 000000000..aefae1855 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_array.go @@ -0,0 +1,672 @@ +package otto + +import ( + "strconv" + "strings" +) + +// Array + +func builtinArray(call FunctionCall) Value { + return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList)) +} + +func builtinNewArray(self *_object, _ Value, 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(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) + } + 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._valueType { + case valueEmpty, valueUndefined, valueNull: + default: + object := call.runtime.toObject(value) + toLocaleString := object.get("toLocaleString") + if !toLocaleString.isCallable() { + panic(newTypeError()) + } + stringValue = toLocaleString.call(toValue_object(object)).toString() + } + 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._valueType { + case valueObject: + object := item._object() + if isArray(object) { + length := toInteger(object.get("length")).value + 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 UndefinedValue() + } + 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 UndefinedValue() + } + 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 = toString(argument) + } + } + 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._valueType { + case valueEmpty, valueUndefined, valueNull: + default: + stringValue = toString(value) + } + 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 := valueToRangeIndex(call.Argument(1), int64(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 (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 = toString(x) + k.value = toString(y) + + if j.value == k.value { + return 0 + } else if j.value < k.value { + return -1 + } + + return 1 + } + + return int(toInt32(compare.Call(UndefinedValue(), []Value{x, y}))) +} + +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 { + arraySortSwap(thisObject, pivot, right) // Right is now the pivot value + cursor := left + for index := left; index < right; index++ { + if sortCompare(thisObject, index, right, compare) == -1 { // Compare to the pivot value + arraySortSwap(thisObject, index, cursor) + cursor += 1 + } + } + arraySortSwap(thisObject, cursor, right) + return cursor +} + +func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) { + if left < right { + pivot := left + (right-left)/2 + pivot = arraySortQuickPartition(thisObject, left, right, pivot, compare) + if pivot > 0 { + arraySortQuickSort(thisObject, left, pivot-1, compare) + } + arraySortQuickSort(thisObject, pivot+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(newTypeError()) + } + 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 = toInteger(call.Argument(1)).value + } + 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 = toInteger(call.Argument(1)).value + } + 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(callThis, value, toValue_int64(index), this).isTrue() { + continue + } + return FalseValue() + } + } + return TrueValue() + } + panic(newTypeError()) +} + +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(callThis, value, toValue_int64(index), this).isTrue() { + return TrueValue() + } + } + } + return FalseValue() + } + panic(newTypeError()) +} + +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(callThis, thisObject.get(key), toValue_int64(index), this) + } + } + return UndefinedValue() + } + panic(newTypeError()) +} + +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(callThis, thisObject.get(key), index, this) + } else { + values[index] = UndefinedValue() + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(newTypeError()) +} + +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(callThis, value, index, this).isTrue() { + values = append(values, value) + } + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(newTypeError()) +} + +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(UndefinedValue(), accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(newTypeError()) +} + +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 -= 1 + break + } + } + } else { + accumulator = start + } + for ; index >= 0; index-- { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = iterator.call(UndefinedValue(), accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(newTypeError()) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_boolean.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_boolean.go new file mode 100644 index 000000000..1aec693ce --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_boolean.go @@ -0,0 +1,28 @@ +package otto + +// Boolean + +func builtinBoolean(call FunctionCall) Value { + return toValue_bool(toBoolean(call.Argument(0))) +} + +func builtinNewBoolean(self *_object, _ Value, 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(toString(value)) +} + +func builtinBoolean_valueOf(call FunctionCall) Value { + value := call.This + if !value.IsBoolean() { + value = call.thisClassObject("Boolean").primitiveValue() + } + return value +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_date.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_date.go new file mode 100644 index 000000000..439d367bc --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_date.go @@ -0,0 +1,616 @@ +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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local))) +} + +func builtinDate_toString(call FunctionCall) Value { + date := dateObjectOf(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.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.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.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.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 := toFloat(value) + if math.IsNaN(value) || math.IsInf(value, 0) { + return NullValue() + } + } + toISOString := object.get("toISOString") + if !toISOString.isCallable() { + panic(newTypeError()) + } + return toISOString.call(toValue_object(object), []Value{}) +} + +func builtinDate_toGMTString(call FunctionCall) Value { + date := dateObjectOf(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.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(object) + date.Set(toFloat(call.Argument(0))) + object.value = date + return date.Value() +} + +func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) { + object := call.thisObject() + date := dateObjectOf(object) + 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] + if value.IsNaN() { + object.value = invalidDateObject + return nil, nil, nil, nil + } + integer := toInteger(value) + if !integer.valid() { + object.value = invalidDateObject + return nil, nil, nil, nil + } + valueList[index] = int(integer.value) + } + baseTime := date.Time() + if timeLocal { + baseTime = baseTime.Local() + } + ecmaTime := ecmaTime(baseTime) + return object, &date, &ecmaTime, valueList +} + +func builtinDate_parse(call FunctionCall) Value { + date := toString(call.Argument(0)) + 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.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.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.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.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.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.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Year()) +} + +func builtinDate_getUTCFullYear(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Year()) +} + +func builtinDate_getMonth(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Local().Month())) +} + +func builtinDate_getUTCMonth(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Month())) +} + +func builtinDate_getDate(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Day()) +} + +func builtinDate_getUTCDate(call FunctionCall) Value { + date := dateObjectOf(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.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Local().Weekday())) +} + +func builtinDate_getUTCDay(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Weekday())) +} + +func builtinDate_getHours(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Hour()) +} + +func builtinDate_getUTCHours(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Hour()) +} + +func builtinDate_getMinutes(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Minute()) +} + +func builtinDate_getUTCMinutes(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Minute()) +} + +func builtinDate_getSeconds(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Second()) +} + +func builtinDate_getUTCSeconds(call FunctionCall) Value { + date := dateObjectOf(call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Second()) +} + +func builtinDate_getMilliseconds(call FunctionCall) Value { + date := dateObjectOf(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.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100)) +} + +func builtinDate_getTimezoneOffset(call FunctionCall) Value { + date := dateObjectOf(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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_error.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_error.go new file mode 100644 index 000000000..effd7f698 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_error.go @@ -0,0 +1,85 @@ +package otto + +func (runtime *_runtime) newEvalError(message Value) *_object { + self := runtime.newErrorObject(message) + 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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newTypeError(message Value) *_object { + self := runtime.newErrorObject(message) + 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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newRangeError(message Value) *_object { + self := runtime.newErrorObject(message) + 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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newURIError(message Value) *_object { + self := runtime.newErrorObject(message) + self.prototype = runtime.Global.URIErrorPrototype + return self +} + +func (runtime *_runtime) newReferenceError(message Value) *_object { + self := runtime.newErrorObject(message) + 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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newSyntaxError(message Value) *_object { + self := runtime.newErrorObject(message) + 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, _ Value, 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, _ Value, argumentList []Value) Value { + return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_function.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_function.go new file mode 100644 index 000000000..80442104d --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_function.go @@ -0,0 +1,117 @@ +package otto + +import ( + "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, _ Value, 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(toString(value), 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, toString(value)) + } + parameterList = strings.Join(tmp, ",") + body = toString(argumentList[count-1]) + } + + function, err := parser.ParseFunction(parameterList, body) + runtime.parseThrow(err) // Will panic/throw appropriately + cmpl_function := parseExpression(function) + + return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.GlobalEnvironment) +} + +func builtinFunction_toString(call FunctionCall) Value { + object := call.thisClassObject("Function") // Should throw a TypeError unless Function + return toValue_string(object.value.(_functionObject).source(object)) +} + +func builtinFunction_apply(call FunctionCall) Value { + if !call.This.isCallable() { + panic(newTypeError()) + } + this := call.Argument(0) + if this.IsUndefined() { + // FIXME Not ECMA5 + this = toValue_object(call.runtime.GlobalObject) + } + argumentList := call.Argument(1) + switch argumentList._valueType { + case valueUndefined, valueNull: + return call.thisObject().Call(this, []Value{}) + case valueObject: + default: + panic(newTypeError()) + } + + 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) +} + +func builtinFunction_call(call FunctionCall) Value { + if !call.This.isCallable() { + panic(newTypeError()) + } + 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:]) + } + return thisObject.Call(this, []Value{}) +} + +func builtinFunction_bind(call FunctionCall) Value { + target := call.This + if !target.isCallable() { + panic(newTypeError()) + } + 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)) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_json.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_json.go new file mode 100644 index 000000000..2501c242e --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_json.go @@ -0,0 +1,285 @@ +package otto + +import ( + "bytes" + "encoding/json" + "math" + "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(toString(call.Argument(0))), &root) + if err != nil { + panic(newSyntaxError(err.Error())) + } + value, exists := builtinJSON_parseWalk(ctx, root) + if !exists { + value = UndefinedValue() + } + 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(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._valueType { + case valueObject: + switch value.value.(*_object).class { + case "String": + case "Number": + default: + continue + } + case valueString: + case valueNumber: + default: + continue + } + name := toString(value) + 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._valueType == valueObject { + switch spaceValue.value.(*_object).class { + case "String": + spaceValue = toValue_string(toString(spaceValue)) + case "Number": + spaceValue = toNumber(spaceValue) + } + } + switch spaceValue._valueType { + case valueString: + value := toString(spaceValue) + if len(value) > 10 { + ctx.gap = value[0:10] + } else { + ctx.gap = value + } + case valueNumber: + value := toInteger(spaceValue).value + 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 UndefinedValue() + } + valueJSON, err := json.Marshal(value) + if err != nil { + panic(newTypeError(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(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(toValue_object(holder), key, value) + } + + if value._valueType == valueObject { + switch value.value.(*_object).class { + case "Boolean": + value = value._object().value.(Value) + case "String": + value = toValue_string(toString(value)) + case "Number": + value = toNumber(value) + } + } + + switch value._valueType { + case valueBoolean: + return toBoolean(value), true + case valueString: + return toString(value), true + case valueNumber: + value := toFloat(value) + if math.IsNaN(value) || math.IsInf(value, 0) { + return nil, true + } + return value, 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(newTypeError("Converting circular structure to JSON")) + } + } + ctx.stack = append(ctx.stack, value) + defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }() + } + if isArray(holder) { + length := holder.get("length").value.(uint32) + 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_math.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_math.go new file mode 100644 index 000000000..37f7d8c85 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_math.go @@ -0,0 +1,145 @@ +package otto + +import ( + "math" + "math/rand" +) + +// Math + +func builtinMath_abs(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Abs(number)) +} + +func builtinMath_acos(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Acos(number)) +} + +func builtinMath_asin(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Asin(number)) +} + +func builtinMath_atan(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Atan(number)) +} + +func builtinMath_atan2(call FunctionCall) Value { + y := toFloat(call.Argument(0)) + if math.IsNaN(y) { + return NaNValue() + } + x := toFloat(call.Argument(1)) + if math.IsNaN(x) { + return NaNValue() + } + return toValue_float64(math.Atan2(y, x)) +} + +func builtinMath_cos(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Cos(number)) +} + +func builtinMath_ceil(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Ceil(number)) +} + +func builtinMath_exp(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Exp(number)) +} + +func builtinMath_floor(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Floor(number)) +} + +func builtinMath_log(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + 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(toFloat(call.ArgumentList[0])) + } + result := toFloat(call.ArgumentList[0]) + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := toFloat(value) + 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(toFloat(call.ArgumentList[0])) + } + result := toFloat(call.ArgumentList[0]) + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := toFloat(value) + 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 := toFloat(call.Argument(0)) + y := toFloat(call.Argument(1)) + if math.Abs(x) == 1 && math.IsInf(y, 0) { + return NaNValue() + } + return toValue_float64(math.Pow(x, y)) +} + +func builtinMath_random(call FunctionCall) Value { + return toValue_float64(rand.Float64()) +} + +func builtinMath_round(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + 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 := toFloat(call.Argument(0)) + return toValue_float64(math.Sin(number)) +} + +func builtinMath_sqrt(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Sqrt(number)) +} + +func builtinMath_tan(call FunctionCall) Value { + number := toFloat(call.Argument(0)) + return toValue_float64(math.Tan(number)) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_number.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_number.go new file mode 100644 index 000000000..3905f5bb4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_number.go @@ -0,0 +1,93 @@ +package otto + +import ( + "math" + "strconv" +) + +// Number + +func numberValueFromNumberArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return toNumber(argumentList[0]) + } + return toValue_int(0) +} + +func builtinNumber(call FunctionCall) Value { + return numberValueFromNumberArgumentList(call.ArgumentList) +} + +func builtinNewNumber(self *_object, _ Value, 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(newRangeError("RangeError: toString() radix must be between 2 and 36")) + } + radix = int(integer) + } + if radix == 10 { + return toValue_string(toString(value)) + } + 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(newRangeError("toFixed() precision must be between 0 and 20")) + } + if call.This.IsNaN() { + return toValue_string("NaN") + } + value := toFloat(call.This) + if math.Abs(value) >= 1e21 { + return toValue_string(floatToString(value, 64)) + } + return toValue_string(strconv.FormatFloat(toFloat(call.This), '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(newRangeError("RangeError: toExponential() precision must be greater than 0")) + } + } + return toValue_string(strconv.FormatFloat(toFloat(call.This), '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(toString(call.This)) + } + precision := toIntegerFloat(value) + if 1 > precision { + panic(newRangeError("RangeError: toPrecision() precision must be greater than 1")) + } + return toValue_string(strconv.FormatFloat(toFloat(call.This), 'g', int(precision), 64)) +} + +func builtinNumber_toLocaleString(call FunctionCall) Value { + return builtinNumber_toString(call) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_object.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_object.go new file mode 100644 index 000000000..2c3648856 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_object.go @@ -0,0 +1,289 @@ +package otto + +import ( + "fmt" +) + +// Object + +func builtinObject(call FunctionCall) Value { + value := call.Argument(0) + switch value._valueType { + case valueUndefined, valueNull: + return toValue_object(call.runtime.newObject()) + } + + return toValue_object(call.runtime.toObject(value)) +} + +func builtinNewObject(self *_object, _ Value, argumentList []Value) Value { + value := valueOfArrayIndex(argumentList, 0) + switch value._valueType { + 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 := toString(call.Argument(0)) + 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 := toString(call.Argument(0)) + 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(newTypeError()) + } + return toString.call(call.This) +} + +func builtinObject_getPrototypeOf(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(newTypeError()) + } + + 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(newTypeError()) + } + + name := toString(call.Argument(1)) + descriptor := object.getOwnProperty(name) + if descriptor == nil { + return UndefinedValue() + } + return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor)) +} + +func builtinObject_defineProperty(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(newTypeError()) + } + name := toString(call.Argument(1)) + descriptor := toPropertyDescriptor(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(newTypeError()) + } + + properties := call.runtime.toObject(call.Argument(1)) + properties.enumerate(false, func(name string) bool { + descriptor := toPropertyDescriptor(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(newTypeError()) + } + + 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(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(newTypeError()) +} + +func builtinObject_preventExtensions(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.extensible = false + } else { + panic(newTypeError()) + } + 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(newTypeError()) +} + +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(newTypeError()) + } + 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(newTypeError()) +} + +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(newTypeError()) + } + 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(newTypeError()) +} + +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(newTypeError()) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_regexp.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_regexp.go new file mode 100644 index 000000000..29d009dc2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_regexp.go @@ -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, _ Value, 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 := toString(thisObject.get("source")) + flags := []byte{} + if toBoolean(thisObject.get("global")) { + flags = append(flags, 'g') + } + if toBoolean(thisObject.get("ignoreCase")) { + flags = append(flags, 'i') + } + if toBoolean(thisObject.get("multiline")) { + flags = append(flags, 'm') + } + return toValue_string(fmt.Sprintf("/%s/%s", source, flags)) +} + +func builtinRegExp_exec(call FunctionCall) Value { + thisObject := call.thisObject() + target := toString(call.Argument(0)) + 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 := toString(call.Argument(0)) + 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 UndefinedValue() +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_string.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_string.go new file mode 100644 index 000000000..e2c6a620c --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_string.go @@ -0,0 +1,504 @@ +package otto + +import ( + "bytes" + "regexp" + "strconv" + "strings" + "unicode/utf8" +) + +// String + +func stringValueFromStringArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return toValue_string(toString(argumentList[0])) + } + return toValue_string("") +} + +func builtinString(call FunctionCall) Value { + return stringValueFromStringArgumentList(call.ArgumentList) +} + +func builtinNewString(self *_object, _ Value, 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.This) + idx := int(toInteger(call.Argument(0)).value) + 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.This) + idx := int(toInteger(call.Argument(0)).value) + 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.This) + var value bytes.Buffer + value.WriteString(toString(call.This)) + for _, item := range call.ArgumentList { + value.WriteString(toString(item)) + } + return toValue_string(value.String()) +} + +func builtinString_indexOf(call FunctionCall) Value { + checkObjectCoercible(call.This) + value := toString(call.This) + target := toString(call.Argument(0)) + 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.This) + value := toString(call.This) + target := toString(call.Argument(0)) + 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 := toInteger(call.ArgumentList[1]) + if !start.valid() { + // startNumber is infinity, so start is the end of string (start = length) + return toValue_int(strings.LastIndex(value, target)) + } + if 0 > start.value { + start.value = 0 + } + end := int(start.value) + len(target) + if end > length { + end = length + } + return toValue_int(strings.LastIndex(value[:end], target)) +} + +func builtinString_match(call FunctionCall) Value { + checkObjectCoercible(call.This) + target := toString(call.This) + matcherValue := call.Argument(0) + matcher := matcherValue._object() + if !matcherValue.IsObject() || matcher.class != "RegExp" { + matcher = call.runtime.newRegExp(matcherValue, UndefinedValue()) + } + global := toBoolean(matcher.get("global")) + 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 UndefinedValue() // !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.This) + target := []byte(toString(call.This)) + 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(toString(searchValue))) + } + + 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] = UndefinedValue() + } + } + argumentList[matchCount+0] = toValue_int(match[0]) + argumentList[matchCount+1] = toValue_string(target) + replacement := toString(replace.Call(UndefinedValue(), argumentList)) + result = append(result, []byte(replacement)...) + lastIndex = match[1] + } + + } else { + replace := []byte(toString(replaceValue)) + 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)) + } + + return UndefinedValue() +} + +func builtinString_search(call FunctionCall) Value { + checkObjectCoercible(call.This) + target := toString(call.This) + searchValue := call.Argument(0) + search := searchValue._object() + if !searchValue.IsObject() || search.class != "RegExp" { + search = call.runtime.newRegExp(searchValue, UndefinedValue()) + } + 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.This) + target := toString(call.This) + + 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 := UndefinedValue() + 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 := toString(separatorValue) + + 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)) + } + + return UndefinedValue() +} + +func builtinString_slice(call FunctionCall) Value { + checkObjectCoercible(call.This) + target := toString(call.This) + + 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.This) + target := toString(call.This) + + 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 := toString(call.This) + + 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.This) + return toValue_string(strings.ToLower(toString(call.This))) +} + +func builtinString_toUpperCase(call FunctionCall) Value { + checkObjectCoercible(call.This) + return toValue_string(strings.ToUpper(toString(call.This))) +} + +// 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.This) + return toValue(strings.Trim(toString(call.This), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimLeft(call FunctionCall) Value { + checkObjectCoercible(call.This) + return toValue(strings.TrimLeft(toString(call.This), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimRight(call FunctionCall) Value { + checkObjectCoercible(call.This) + return toValue(strings.TrimRight(toString(call.This), + builtinString_trim_whitespace)) +} + +func builtinString_localeCompare(call FunctionCall) Value { + checkObjectCoercible(call.This) + this := toString(call.This) + that := toString(call.Argument(0)) + 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(toString(call.This), isWhiteSpaceOrLineTerminator)) +} +*/ + +func builtinString_toLocaleLowerCase(call FunctionCall) Value { + return builtinString_toLowerCase(call) +} + +func builtinString_toLocaleUpperCase(call FunctionCall) Value { + return builtinString_toUpperCase(call) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/builtin_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_test.go new file mode 100644 index 000000000..f5be00ab6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/builtin_test.go @@ -0,0 +1,136 @@ +package otto + +import ( + "testing" +) + +func TestString_substr(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + "abc".substr(0,1), // "a" + "abc".substr(0,2), // "ab" + "abc".substr(0,3), // "abc" + "abc".substr(0,4), // "abc" + "abc".substr(0,9), // "abc" + ]; + `, "a,ab,abc,abc,abc") + + test(` + [ + "abc".substr(1,1), // "b" + "abc".substr(1,2), // "bc" + "abc".substr(1,3), // "bc" + "abc".substr(1,4), // "bc" + "abc".substr(1,9), // "bc" + ]; + `, "b,bc,bc,bc,bc") + + test(` + [ + "abc".substr(2,1), // "c" + "abc".substr(2,2), // "c" + "abc".substr(2,3), // "c" + "abc".substr(2,4), // "c" + "abc".substr(2,9), // "c" + ]; + `, "c,c,c,c,c") + + test(` + [ + "abc".substr(3,1), // "" + "abc".substr(3,2), // "" + "abc".substr(3,3), // "" + "abc".substr(3,4), // "" + "abc".substr(3,9), // "" + ]; + `, ",,,,") + + test(` + [ + "abc".substr(0), // "abc" + "abc".substr(1), // "bc" + "abc".substr(2), // "c" + "abc".substr(3), // "" + "abc".substr(9), // "" + ]; + `, "abc,bc,c,,") + + test(` + [ + "abc".substr(-9), // "abc" + "abc".substr(-3), // "abc" + "abc".substr(-2), // "bc" + "abc".substr(-1), // "c" + ]; + `, "abc,abc,bc,c") + + test(` + [ + "abc".substr(-9, 1), // "a" + "abc".substr(-3, 1), // "a" + "abc".substr(-2, 1), // "b" + "abc".substr(-1, 1), // "c" + "abc".substr(-1, 2), // "c" + ]; + `, "a,a,b,c,c") + + test(`"abcd".substr(3, 5)`, "d") + }) +} + +func Test_builtin_escape(t *testing.T) { + tt(t, func() { + is(builtin_escape("abc"), "abc") + + is(builtin_escape("="), "%3D") + + is(builtin_escape("abc=%+32"), "abc%3D%25+32") + + is(builtin_escape("世界"), "%u4E16%u754C") + }) +} + +func Test_builtin_unescape(t *testing.T) { + tt(t, func() { + is(builtin_unescape("abc"), "abc") + + is(builtin_unescape("=%3D"), "==") + + is(builtin_unescape("abc%3D%25+32"), "abc=%+32") + + is(builtin_unescape("%u4E16%u754C"), "世界") + }) +} + +func TestGlobal_escape(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + escape("abc"), // "abc" + escape("="), // "%3D" + escape("abc=%+32"), // "abc%3D%25+32" + escape("\u4e16\u754c"), // "%u4E16%u754C" + ]; + `, "abc,%3D,abc%3D%25+32,%u4E16%u754C") + }) +} + +func TestGlobal_unescape(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + unescape("abc"), // "abc" + unescape("=%3D"), // "==" + unescape("abc%3D%25+32"), // "abc=%+32" + unescape("%u4E16%u754C"), // "世界" + ]; + `, "abc,==,abc=%+32,世界") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/clone.go b/Godeps/_workspace/src/github.com/obscuren/otto/clone.go new file mode 100644 index 000000000..fbdde81c2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/clone.go @@ -0,0 +1,144 @@ +package otto + +import ( + "fmt" +) + +type _clone struct { + runtime *_runtime + stash struct { + object map[*_object]*_object + objectEnvironment map[*_objectEnvironment]*_objectEnvironment + declarativeEnvironment map[*_declarativeEnvironment]*_declarativeEnvironment + } +} + +func (runtime *_runtime) clone() *_runtime { + + self := &_runtime{} + clone := &_clone{ + runtime: self, + } + clone.stash.object = make(map[*_object]*_object) + clone.stash.objectEnvironment = make(map[*_objectEnvironment]*_objectEnvironment) + clone.stash.declarativeEnvironment = make(map[*_declarativeEnvironment]*_declarativeEnvironment) + + globalObject := clone.object(runtime.GlobalObject) + self.GlobalEnvironment = self.newObjectEnvironment(globalObject, nil) + self.GlobalObject = globalObject + self.Global = _global{ + clone.object(runtime.Global.Object), + clone.object(runtime.Global.Function), + clone.object(runtime.Global.Array), + clone.object(runtime.Global.String), + clone.object(runtime.Global.Boolean), + clone.object(runtime.Global.Number), + clone.object(runtime.Global.Math), + clone.object(runtime.Global.Date), + clone.object(runtime.Global.RegExp), + clone.object(runtime.Global.Error), + clone.object(runtime.Global.EvalError), + clone.object(runtime.Global.TypeError), + clone.object(runtime.Global.RangeError), + clone.object(runtime.Global.ReferenceError), + clone.object(runtime.Global.SyntaxError), + clone.object(runtime.Global.URIError), + clone.object(runtime.Global.JSON), + + clone.object(runtime.Global.ObjectPrototype), + clone.object(runtime.Global.FunctionPrototype), + clone.object(runtime.Global.ArrayPrototype), + clone.object(runtime.Global.StringPrototype), + clone.object(runtime.Global.BooleanPrototype), + clone.object(runtime.Global.NumberPrototype), + clone.object(runtime.Global.DatePrototype), + clone.object(runtime.Global.RegExpPrototype), + clone.object(runtime.Global.ErrorPrototype), + clone.object(runtime.Global.EvalErrorPrototype), + clone.object(runtime.Global.TypeErrorPrototype), + clone.object(runtime.Global.RangeErrorPrototype), + clone.object(runtime.Global.ReferenceErrorPrototype), + clone.object(runtime.Global.SyntaxErrorPrototype), + clone.object(runtime.Global.URIErrorPrototype), + } + + self.EnterGlobalExecutionContext() + + self.eval = self.GlobalObject.property["eval"].value.(Value).value.(*_object) + self.GlobalObject.prototype = self.Global.ObjectPrototype + + return self +} +func (clone *_clone) object(self0 *_object) *_object { + if self1, exists := clone.stash.object[self0]; exists { + return self1 + } + self1 := &_object{} + clone.stash.object[self0] = self1 + return self0.objectClass.clone(self0, self1, clone) +} + +func (clone *_clone) declarativeEnvironment(self0 *_declarativeEnvironment) (*_declarativeEnvironment, bool) { + if self1, exists := clone.stash.declarativeEnvironment[self0]; exists { + return self1, true + } + self1 := &_declarativeEnvironment{} + clone.stash.declarativeEnvironment[self0] = self1 + return self1, false +} + +func (clone *_clone) objectEnvironment(self0 *_objectEnvironment) (*_objectEnvironment, bool) { + if self1, exists := clone.stash.objectEnvironment[self0]; exists { + return self1, true + } + self1 := &_objectEnvironment{} + clone.stash.objectEnvironment[self0] = self1 + return self1, false +} + +func (clone *_clone) value(self0 Value) Value { + self1 := self0 + switch value := self0.value.(type) { + case *_object: + self1.value = clone.object(value) + } + return self1 +} + +func (clone *_clone) valueArray(self0 []Value) []Value { + self1 := make([]Value, len(self0)) + for index, value := range self0 { + self1[index] = clone.value(value) + } + return self1 +} + +func (clone *_clone) environment(self0 _environment) _environment { + if self0 == nil { + return nil + } + return self0.clone(clone) +} + +func (clone *_clone) property(self0 _property) _property { + self1 := self0 + if value, valid := self0.value.(Value); valid { + self1.value = clone.value(value) + } else { + panic(fmt.Errorf("self0.value.(Value) != true")) + } + return self1 +} + +func (clone *_clone) declarativeProperty(self0 _declarativeProperty) _declarativeProperty { + self1 := self0 + self1.value = clone.value(self0.value) + return self1 +} + +func (clone *_clone) callFunction(self0 _callFunction) _callFunction { + if self0 == nil { + return nil + } + return self0.clone(clone) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate.go new file mode 100644 index 000000000..a5b30f6c0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate.go @@ -0,0 +1,87 @@ +package otto + +import ( + "strconv" +) + +func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram) Value { + self.cmpl_functionDeclaration(node.functionList) + self.cmpl_variableDeclaration(node.varList) + return self.cmpl_evaluate_nodeStatementList(node.body) +} + +func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_functionEnvironment, 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 := UndefinedValue() + if index < len(argumentList) { + value = argumentList[index] + indexOfParameterName[index] = name + } + self.localSet(name, value) + } + + if !argumentsFound { + arguments := self.newArgumentsObject(indexOfParameterName, environment, len(argumentList)) + arguments.defineProperty("callee", toValue_object(function), 0101, false) + environment.arguments = arguments + self.localSet("arguments", toValue_object(arguments)) + 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.isResult() { + return result + } + + return UndefinedValue() +} + +func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) { + executionContext := self._executionContext(0) + eval := executionContext.eval + environment := executionContext.VariableEnvironment + + for _, function := range list { + name := function.name + value := self.cmpl_evaluate_nodeExpression(function) + if !environment.HasBinding(name) { + environment.CreateMutableBinding(name, eval == true) + } + // TODO 10.5.5.e + environment.SetMutableBinding(name, value, false) // TODO strict + } +} + +func (self *_runtime) cmpl_variableDeclaration(list []string) { + executionContext := self._executionContext(0) + eval := executionContext.eval + environment := executionContext.VariableEnvironment + + for _, name := range list { + if !environment.HasBinding(name) { + environment.CreateMutableBinding(name, eval == true) + environment.SetMutableBinding(name, UndefinedValue(), false) // TODO strict + } + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_expression.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_expression.go new file mode 100644 index 000000000..657667b36 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_expression.go @@ -0,0 +1,391 @@ +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.LexicalEnvironment() + if node.name != "" { + local = self.newDeclarativeEnvironment(local) + } + + value := toValue_object(self.newNodeFunction(node, local)) + if node.name != "" { + local.CreateMutableBinding(node.name, false) + local.SetMutableBinding(node.name, value, false) + } + 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.LexicalEnvironment(), name, false) + 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._executionContext(0).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, Value{}) + } else { + valueArray = append(valueArray, self.GetValue(self.cmpl_evaluate_nodeExpression(node))) + } + } + + 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 := self.GetValue(right) + + 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 := self.GetValue(left) + + switch node.operator { + // Logical + case token.LOGICAL_AND: + if !toBoolean(leftValue) { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return self.GetValue(right) + case token.LOGICAL_OR: + if toBoolean(leftValue) { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return self.GetValue(right) + } + + return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value { + + left := self.GetValue(self.cmpl_evaluate_nodeExpression(node.left)) + right := self.GetValue(self.cmpl_evaluate_nodeExpression(node.right)) + + 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 := self.GetValue(target) + member := self.cmpl_evaluate_nodeExpression(node.member) + memberValue := self.GetValue(member) + + // TODO Pass in base value as-is, and defer toObject till later? + return toValue(newPropertyReference(self.toObject(targetValue), toString(memberValue), false)) +} + +func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value { + callee := self.cmpl_evaluate_nodeExpression(node.callee) + calleeValue := self.GetValue(callee) + argumentList := []Value{} + if withArgumentList != nil { + argumentList = self.toValueArray(withArgumentList...) + } else { + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.GetValue(self.cmpl_evaluate_nodeExpression(argumentNode))) + } + } + this := UndefinedValue() + calleeReference := callee.reference() + evalHint := false + if calleeReference != nil { + if calleeReference.IsPropertyReference() { + calleeObject := calleeReference.GetBase().(*_object) + this = toValue_object(calleeObject) + } else { + // TODO ImplictThisValue + } + if calleeReference.GetName() == "eval" { + evalHint = true // Possible direct eval + } + } + if !calleeValue.IsFunction() { + panic(newTypeError("%v is not a function", calleeValue)) + } + return self.Call(calleeValue._object(), this, argumentList, evalHint) +} + +func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := self.GetValue(test) + if toBoolean(testValue) { + 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 := self.GetValue(target) + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(newTypeError(fmt.Sprintf("Cannot access member '%s' of %s", node.identifier, err.Error()))) + } + return toValue(newPropertyReference(object, node.identifier, false)) +} + +func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value { + callee := self.cmpl_evaluate_nodeExpression(node.callee) + calleeValue := self.GetValue(callee) + argumentList := []Value{} + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.GetValue(self.cmpl_evaluate_nodeExpression(argumentNode))) + } + this := UndefinedValue() + if !calleeValue.IsFunction() { + panic(newTypeError("%v is not a function", calleeValue)) + } + return calleeValue._object().Construct(this, 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.GetValue(self.cmpl_evaluate_nodeExpression(property.value)), 0111, false) + case "get": + getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.LexicalEnvironment()) + 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.LexicalEnvironment()) + 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 = self.GetValue(result) + } + 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._valueType == valueReference && target.reference().IsUnresolvable() { + if node.operator == token.TYPEOF { + return toValue_string("undefined") + } + return TrueValue() + } + } + + switch node.operator { + case token.NOT: + targetValue := self.GetValue(target) + if targetValue.toBoolean() { + return FalseValue() + } + return TrueValue() + case token.BITWISE_NOT: + targetValue := self.GetValue(target) + integerValue := toInt32(targetValue) + return toValue_int32(^integerValue) + case token.PLUS: + targetValue := self.GetValue(target) + return toValue_float64(targetValue.toFloat()) + case token.MINUS: + targetValue := self.GetValue(target) + value := targetValue.toFloat() + // TODO Test this + sign := float64(-1) + if math.Signbit(value) { + sign = 1 + } + return toValue_float64(math.Copysign(value, sign)) + case token.INCREMENT: + targetValue := self.GetValue(target) + if node.postfix { + // Postfix++ + oldValue := targetValue.toFloat() + newValue := toValue_float64(+1 + oldValue) + self.PutValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // ++Prefix + newValue := toValue_float64(+1 + targetValue.toFloat()) + self.PutValue(target.reference(), newValue) + return newValue + } + case token.DECREMENT: + targetValue := self.GetValue(target) + if node.postfix { + // Postfix-- + oldValue := targetValue.toFloat() + newValue := toValue_float64(-1 + oldValue) + self.PutValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // --Prefix + newValue := toValue_float64(-1 + targetValue.toFloat()) + self.PutValue(target.reference(), newValue) + return newValue + } + case token.VOID: + self.GetValue(target) // FIXME Side effect? + return UndefinedValue() + case token.DELETE: + reference := target.reference() + if reference == nil { + return TrueValue() + } + return toValue_bool(target.reference().Delete()) + case token.TYPEOF: + targetValue := self.GetValue(target) + switch targetValue._valueType { + 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().functionValue().call != nil { + 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.LexicalEnvironment(), node.name, false) + right := self.cmpl_evaluate_nodeExpression(node.initializer) + rightValue := self.GetValue(right) + + self.PutValue(left, rightValue) + } + return toValue_string(node.name) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_statement.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_statement.go new file mode 100644 index 000000000..6add2baea --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_evaluate_statement.go @@ -0,0 +1,410 @@ +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: + // FIXME If result is break, then return the empty value? + return self.cmpl_evaluate_nodeStatementList(node.list) + + 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: + return Value{} // Nothing happens. + + case *_nodeDoWhileStatement: + return self.cmpl_evaluate_nodeDoWhileStatement(node) + + case *_nodeEmptyStatement: + return Value{} + + 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.GetValue(self.cmpl_evaluate_nodeExpression(node.argument)))) + } + return toValue(newReturnResult(UndefinedValue())) + + case *_nodeSwitchStatement: + return self.cmpl_evaluate_nodeSwitchStatement(node) + + case *_nodeThrowStatement: + value := self.GetValue(self.cmpl_evaluate_nodeExpression(node.argument)) + 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 Value{} + + 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._valueType { + 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 = self.GetValue(value) + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + test := node.test + + result := Value{} +resultBreak: + for { + for _, node := range node.body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value._valueType { + 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.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() { + // 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 := self.GetValue(source) + + switch sourceValue._valueType { + case valueUndefined, valueNull: + return emptyValue() + } + + sourceObject := self.toObject(sourceValue) + + into := node.into + body := node.body + + result := Value{} + object := sourceObject + for object != nil { + enumerateValue := Value{} + 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 := toString(into) + // TODO Should be true or false (strictness) depending on context + into = toValue(getIdentifierReference(self.LexicalEnvironment(), identifier, false)) + } + self.PutValue(into.reference(), toValue_string(name)) + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value._valueType { + 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) + self.GetValue(initialResult) // Side-effect trigger + } + + result := Value{} +resultBreak: + for { + if test != nil { + testResult := self.cmpl_evaluate_nodeExpression(test) + testResultValue := self.GetValue(testResult) + if toBoolean(testResultValue) == false { + break + } + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value._valueType { + 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) + self.GetValue(updateResult) // Side-effect trigger + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := self.GetValue(test) + if toBoolean(testValue) { + return self.cmpl_evaluate_nodeStatement(node.consequent) + } else if node.alternate != nil { + return self.cmpl_evaluate_nodeStatement(node.alternate) + } + + return Value{} +} + +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 := Value{} + if target != -1 { + for _, clause := range node.body[target:] { + for _, statement := range clause.consequent { + value := self.cmpl_evaluate_nodeStatement(statement) + switch value._valueType { + case valueResult: + switch value.evaluateBreak(labels) { + case resultReturn: + return value + case resultBreak: + return Value{} + } + 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 { + + lexicalEnvironment := self._executionContext(0).newDeclarativeEnvironment(self) + defer func() { + self._executionContext(0).LexicalEnvironment = lexicalEnvironment + }() + // TODO If necessary, convert TypeError => TypeError + // That, is, such errors can be thrown despite not being JavaScript "native" + self.localSet(node.catch.parameter, tryCatchValue) + + // 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.isResult() { + 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 := Value{} +resultBreakContinue: + for { + if !self.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() { + // Stahp: while (false) ... + break + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value._valueType { + 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) + objectValue := self.GetValue(object) + previousLexicalEnvironment, lexicalEnvironment := self._executionContext(0).newLexicalEnvironment(self.toObject(objectValue)) + lexicalEnvironment.ProvideThis = true + defer func() { + self._executionContext(0).LexicalEnvironment = previousLexicalEnvironment + }() + + return self.cmpl_evaluate_nodeStatement(node.body) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_function.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_function.go new file mode 100644 index 000000000..0c8a9df14 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_function.go @@ -0,0 +1,46 @@ +package otto + +// _cmpl_nodeCallFunction +type _cmpl_nodeCallFunction struct { + node *_nodeFunctionLiteral + scopeEnvironment _environment // Can be either Lexical or Variable +} + +func new_nodeCallFunction(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_cmpl_nodeCallFunction { + self := &_cmpl_nodeCallFunction{ + node: node, + } + self.scopeEnvironment = scopeEnvironment + return self +} + +func (self _cmpl_nodeCallFunction) Dispatch(function *_object, environment *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value { + return runtime.cmpl_call_nodeFunction(function, environment, self.node, this, argumentList) +} + +func (self _cmpl_nodeCallFunction) ScopeEnvironment() _environment { + return self.scopeEnvironment +} + +func (self _cmpl_nodeCallFunction) Source(object *_object) string { + return self.node.source +} + +func (self0 _cmpl_nodeCallFunction) clone(clone *_clone) _callFunction { + return _cmpl_nodeCallFunction{ + node: self0.node, + scopeEnvironment: clone.environment(self0.scopeEnvironment), + } +} + +// --- + +func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_object { + self := runtime.newClassObject("Function") + self.value = _functionObject{ + call: new_nodeCallFunction(node, scopeEnvironment), + construct: defaultConstructFunction, + } + self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) + return self +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_parse.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_parse.go new file mode 100644 index 000000000..7ce1af57d --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_parse.go @@ -0,0 +1,630 @@ +package otto + +import ( + "fmt" + "regexp" + + "github.com/robertkrimen/otto/ast" + "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 parseExpression(x ast.Expression) _nodeExpression { + if x == nil { + return nil + } + + switch x := x.(type) { + + case *ast.ArrayLiteral: + y := &_nodeArrayLiteral{ + value: make([]_nodeExpression, len(x.Value)), + } + for i, value := range x.Value { + y.value[i] = parseExpression(value) + } + return y + + case *ast.AssignExpression: + return &_nodeAssignExpression{ + operator: x.Operator, + left: parseExpression(x.Left), + right: parseExpression(x.Right), + } + + case *ast.BinaryExpression: + return &_nodeBinaryExpression{ + operator: x.Operator, + left: parseExpression(x.Left), + right: parseExpression(x.Right), + comparison: x.Comparison, + } + + case *ast.BooleanLiteral: + if x.Value { + return trueLiteral + } + return falseLiteral + + case *ast.BracketExpression: + return &_nodeBracketExpression{ + left: parseExpression(x.Left), + member: parseExpression(x.Member), + } + + case *ast.CallExpression: + y := &_nodeCallExpression{ + callee: parseExpression(x.Callee), + argumentList: make([]_nodeExpression, len(x.ArgumentList)), + } + for i, value := range x.ArgumentList { + y.argumentList[i] = parseExpression(value) + } + return y + + case *ast.ConditionalExpression: + return &_nodeConditionalExpression{ + test: parseExpression(x.Test), + consequent: parseExpression(x.Consequent), + alternate: parseExpression(x.Alternate), + } + + case *ast.DotExpression: + return &_nodeDotExpression{ + left: parseExpression(x.Left), + identifier: x.Identifier.Name, + } + + case *ast.FunctionLiteral: + name := "" + if x.Name != nil { + name = x.Name.Name + } + y := &_nodeFunctionLiteral{ + name: name, + body: parseStatement(x.Body), + source: x.Source, + } + if x.ParameterList != nil { + list := x.ParameterList.List + y.parameterList = make([]string, len(list)) + for i, value := range list { + y.parameterList[i] = value.Name + } + } + for _, value := range x.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + y.varList = append(y.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value)) + } + } + return y + + case *ast.Identifier: + return &_nodeIdentifier{ + name: x.Name, + } + + case *ast.NewExpression: + y := &_nodeNewExpression{ + callee: parseExpression(x.Callee), + argumentList: make([]_nodeExpression, len(x.ArgumentList)), + } + for i, value := range x.ArgumentList { + y.argumentList[i] = parseExpression(value) + } + return y + + case *ast.NullLiteral: + return nullLiteral + + case *ast.NumberLiteral: + return &_nodeLiteral{ + value: toValue(x.Value), + } + + case *ast.ObjectLiteral: + y := &_nodeObjectLiteral{ + value: make([]_nodeProperty, len(x.Value)), + } + for i, value := range x.Value { + y.value[i] = _nodeProperty{ + key: value.Key, + kind: value.Kind, + value: parseExpression(value.Value), + } + } + return y + + case *ast.RegExpLiteral: + return &_nodeRegExpLiteral{ + flags: x.Flags, + pattern: x.Pattern, + } + + case *ast.SequenceExpression: + y := &_nodeSequenceExpression{ + sequence: make([]_nodeExpression, len(x.Sequence)), + } + for i, value := range x.Sequence { + y.sequence[i] = parseExpression(value) + } + return y + + case *ast.StringLiteral: + return &_nodeLiteral{ + value: toValue_string(x.Value), + } + + case *ast.ThisExpression: + return &_nodeThisExpression{} + + case *ast.UnaryExpression: + return &_nodeUnaryExpression{ + operator: x.Operator, + operand: parseExpression(x.Operand), + postfix: x.Postfix, + } + + case *ast.VariableExpression: + return &_nodeVariableExpression{ + name: x.Name, + initializer: parseExpression(x.Initializer), + } + + } + + panic(fmt.Errorf("Here be dragons: parseExpression(%T)", x)) +} + +func parseStatement(x ast.Statement) _nodeStatement { + if x == nil { + return nil + } + + switch x := x.(type) { + + case *ast.BlockStatement: + y := &_nodeBlockStatement{ + list: make([]_nodeStatement, len(x.List)), + } + for i, value := range x.List { + y.list[i] = parseStatement(value) + } + return y + + case *ast.BranchStatement: + y := &_nodeBranchStatement{ + branch: x.Token, + } + if x.Label != nil { + y.label = x.Label.Name + } + return y + + case *ast.DebuggerStatement: + return &_nodeDebuggerStatement{} + + case *ast.DoWhileStatement: + y := &_nodeDoWhileStatement{ + test: parseExpression(x.Test), + } + body := parseStatement(x.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + y.body = block.list + } else { + y.body = append(y.body, body) + } + return y + + case *ast.EmptyStatement: + return emptyStatement + + case *ast.ExpressionStatement: + return &_nodeExpressionStatement{ + expression: parseExpression(x.Expression), + } + + case *ast.ForInStatement: + y := &_nodeForInStatement{ + into: parseExpression(x.Into), + source: parseExpression(x.Source), + } + body := parseStatement(x.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + y.body = block.list + } else { + y.body = append(y.body, body) + } + return y + + case *ast.ForStatement: + y := &_nodeForStatement{ + initializer: parseExpression(x.Initializer), + update: parseExpression(x.Update), + test: parseExpression(x.Test), + } + body := parseStatement(x.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + y.body = block.list + } else { + y.body = append(y.body, body) + } + return y + + case *ast.IfStatement: + return &_nodeIfStatement{ + test: parseExpression(x.Test), + consequent: parseStatement(x.Consequent), + alternate: parseStatement(x.Alternate), + } + + case *ast.LabelledStatement: + return &_nodeLabelledStatement{ + label: x.Label.Name, + statement: parseStatement(x.Statement), + } + + case *ast.ReturnStatement: + return &_nodeReturnStatement{ + argument: parseExpression(x.Argument), + } + + case *ast.SwitchStatement: + y := &_nodeSwitchStatement{ + discriminant: parseExpression(x.Discriminant), + default_: x.Default, + body: make([]*_nodeCaseStatement, len(x.Body)), + } + for i, p := range x.Body { + q := &_nodeCaseStatement{ + test: parseExpression(p.Test), + consequent: make([]_nodeStatement, len(p.Consequent)), + } + for j, value := range p.Consequent { + q.consequent[j] = parseStatement(value) + } + y.body[i] = q + } + return y + + case *ast.ThrowStatement: + return &_nodeThrowStatement{ + argument: parseExpression(x.Argument), + } + + case *ast.TryStatement: + y := &_nodeTryStatement{ + body: parseStatement(x.Body), + finally: parseStatement(x.Finally), + } + if x.Catch != nil { + y.catch = &_nodeCatchStatement{ + parameter: x.Catch.Parameter.Name, + body: parseStatement(x.Catch.Body), + } + } + return y + + case *ast.VariableStatement: + y := &_nodeVariableStatement{ + list: make([]_nodeExpression, len(x.List)), + } + for i, value := range x.List { + y.list[i] = parseExpression(value) + } + return y + + case *ast.WhileStatement: + y := &_nodeWhileStatement{ + test: parseExpression(x.Test), + } + body := parseStatement(x.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + y.body = block.list + } else { + y.body = append(y.body, body) + } + return y + + case *ast.WithStatement: + return &_nodeWithStatement{ + object: parseExpression(x.Object), + body: parseStatement(x.Body), + } + + } + + panic(fmt.Errorf("Here be dragons: parseStatement(%T)", x)) +} + +func cmpl_parse(x *ast.Program) *_nodeProgram { + y := &_nodeProgram{ + body: make([]_nodeStatement, len(x.Body)), + } + for i, value := range x.Body { + y.body[i] = parseStatement(value) + } + for _, value := range x.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + y.varList = append(y.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: parseProgram.DeclarationList(%T)", value)) + } + } + return y +} + +type _nodeProgram struct { + body []_nodeStatement + + varList []string + functionList []*_nodeFunctionLiteral + + variableList []_nodeDeclaration +} + +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 { + left _nodeExpression + member _nodeExpression + } + + _nodeCallExpression struct { + callee _nodeExpression + argumentList []_nodeExpression + } + + _nodeConditionalExpression struct { + test _nodeExpression + consequent _nodeExpression + alternate _nodeExpression + } + + _nodeDotExpression struct { + left _nodeExpression + identifier string + } + + _nodeFunctionLiteral struct { + name string + body _nodeStatement + source string + parameterList []string + varList []string + functionList []*_nodeFunctionLiteral + } + + _nodeIdentifier struct { + 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 { + 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() {} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_test.go new file mode 100644 index 000000000..01ee86e17 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/cmpl_test.go @@ -0,0 +1,54 @@ +package otto + +import ( + "testing" + + "github.com/robertkrimen/otto/parser" +) + +func Test_cmpl(t *testing.T) { + tt(t, func() { + vm := New() + + test := func(src string, expect ...interface{}) { + program, err := parser.ParseFile(nil, "", src, 0) + is(err, nil) + { + program := cmpl_parse(program) + value := vm.runtime.cmpl_evaluate_nodeProgram(program) + if len(expect) > 0 { + is(value, expect[0]) + } + } + } + + test(``, Value{}) + + test(`var abc = 1; abc;`, 1) + + test(`var abc = 1 + 1; abc;`, 2) + + test(`1 + 2;`, 3) + }) +} + +func TestParse_cmpl(t *testing.T) { + tt(t, func() { + + test := func(src string) { + program, err := parser.ParseFile(nil, "", src, 0) + is(err, nil) + is(cmpl_parse(program), "!=", nil) + } + + test(``) + + test(`var abc = 1; abc;`) + + test(` + function abc() { + return; + } + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/console.go b/Godeps/_workspace/src/github.com/obscuren/otto/console.go new file mode 100644 index 000000000..30333ad08 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/console.go @@ -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 UndefinedValue() +} + +func builtinConsole_error(call FunctionCall) Value { + fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) + return UndefinedValue() +} + +// Nothing happens. +func builtinConsole_dir(call FunctionCall) Value { + return UndefinedValue() +} + +func builtinConsole_time(call FunctionCall) Value { + return UndefinedValue() +} + +func builtinConsole_timeEnd(call FunctionCall) Value { + return UndefinedValue() +} + +func builtinConsole_trace(call FunctionCall) Value { + return UndefinedValue() +} + +func builtinConsole_assert(call FunctionCall) Value { + return UndefinedValue() +} + +func (runtime *_runtime) newConsole() *_object { + + return newConsoleObject(runtime) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/date_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/date_test.go new file mode 100644 index 000000000..18ab528db --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/date_test.go @@ -0,0 +1,478 @@ +package otto + +import ( + "math" + "testing" + "time" +) + +func mockTimeLocal(location *time.Location) func() { + local := time.Local + time.Local = location + return func() { + time.Local = local + } +} + +// Passing or failing should not be dependent on what time zone we're in +func mockUTC() func() { + return mockTimeLocal(time.UTC) +} + +func TestDate(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + time0 := time.Unix(1348616313, 47*1000*1000).Local() + + test(`Date`, "function Date() { [native code] }") + test(`new Date(0).toUTCString()`, "Thu, 01 Jan 1970 00:00:00 UTC") + test(`new Date(0).toGMTString()`, "Thu, 01 Jan 1970 00:00:00 GMT") + if false { + // TODO toLocale{Date,Time}String + test(`new Date(0).toLocaleString()`, "") + test(`new Date(0).toLocaleDateString()`, "") + test(`new Date(0).toLocaleTimeString()`, "") + } + test(`new Date(1348616313).getTime()`, 1348616313) + test(`new Date(1348616313).toUTCString()`, "Fri, 16 Jan 1970 14:36:56 UTC") + test(`abc = new Date(1348616313047); abc.toUTCString()`, "Tue, 25 Sep 2012 23:38:33 UTC") + test(`abc.getYear()`, time0.Year()-1900) + test(`abc.getFullYear()`, time0.Year()) + test(`abc.getUTCFullYear()`, 2012) + test(`abc.getMonth()`, int(time0.Month())-1) // Remember, the JavaScript month is 0-based + test(`abc.getUTCMonth()`, 8) + test(`abc.getDate()`, time0.Day()) + test(`abc.getUTCDate()`, 25) + test(`abc.getDay()`, int(time0.Weekday())) + test(`abc.getUTCDay()`, 2) + test(`abc.getHours()`, time0.Hour()) + test(`abc.getUTCHours()`, 23) + test(`abc.getMinutes()`, time0.Minute()) + test(`abc.getUTCMinutes()`, 38) + test(`abc.getSeconds()`, time0.Second()) + test(`abc.getUTCSeconds()`, 33) + test(`abc.getMilliseconds()`, time0.Nanosecond()/(1000*1000)) // In honor of the 47% + test(`abc.getUTCMilliseconds()`, 47) + _, offset := time0.Zone() + test(`abc.getTimezoneOffset()`, offset/-60) + + test(`new Date("Xyzzy").getTime()`, math.NaN()) + + test(`abc.setFullYear(2011); abc.toUTCString()`, "Sun, 25 Sep 2011 23:38:33 UTC") + test(`new Date(12564504e5).toUTCString()`, "Sun, 25 Oct 2009 06:00:00 UTC") + test(`new Date(2009, 9, 25).toUTCString()`, "Sun, 25 Oct 2009 00:00:00 UTC") + test(`+(new Date(2009, 9, 25))`, 1256428800000) + + format := "Mon, 2 Jan 2006 15:04:05 MST" + + time1 := time.Unix(1256450400, 0) + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), 2001*1000*1000, time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMilliseconds(2001); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), 61, time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setSeconds("61"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), 61, time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMinutes("61"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), 5, time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setHours("5"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), 26, time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setDate("26"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), 10, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMonth(9); abc.toUTCString()`, time0.Format(format)) + test(`abc = new Date(12564504e5); abc.setMonth("09"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), 11, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMonth("10"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(2010, time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setFullYear(2010); abc.toUTCString()`, time0.Format(format)) + + test(`new Date("2001-01-01T10:01:02.000").getTime()`, 978343262000) + + // Date() + test(`typeof Date()`, "string") + test(`typeof Date(2006, 1, 2)`, "string") + + test(` + abc = Object.getOwnPropertyDescriptor(Date, "parse"); + [ abc.value === Date.parse, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + + test(` + abc = Object.getOwnPropertyDescriptor(Date.prototype, "toTimeString"); + [ abc.value === Date.prototype.toTimeString, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + + test(` + var abc = Object.getOwnPropertyDescriptor(Date, "prototype"); + [ [ typeof Date.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestDate_parse(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`Date.parse("2001-01-01T10:01:02.000")`, 978343262000) + + test(`Date.parse("2006-01-02T15:04:05.000")`, 1136214245000) + + test(`Date.parse("2006")`, 1136073600000) + + test(`Date.parse("1970-01-16T14:36:56+00:00")`, 1348616000) + + test(`Date.parse("1970-01-16T14:36:56.313+00:00")`, 1348616313) + + test(`Date.parse("1970-01-16T14:36:56.000")`, 1348616000) + + test(`Date.parse.length`, 1) + }) +} + +func TestDate_UTC(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`Date.UTC(2009, 9, 25)`, 1256428800000) + + test(`Date.UTC.length`, 7) + }) +} + +func TestDate_now(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + // FIXME I think this too risky + test(`+(""+Date.now()).substr(0, 10)`, float64(epochToInteger(timeToEpoch(time.Now()))/1000)) + + test(`Date.now() - Date.now(1,2,3) < 24 * 60 * 60`, true) + }) +} + +func TestDate_toISOString(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(0).toISOString()`, "1970-01-01T00:00:00.000Z") + }) +} + +func TestDate_toJSON(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(0).toJSON()`, "1970-01-01T00:00:00.000Z") + }) +} + +func TestDate_setYear(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(12564504e5).setYear(96)`, 846223200000) + + test(`new Date(12564504e5).setYear(1996)`, 846223200000) + + test(`new Date(12564504e5).setYear(2000)`, 972453600000) + }) +} + +func TestDateDefaultValue(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var date = new Date(); + date + 0 === date.toString() + "0"; + `, true) + }) +} + +func TestDate_April1978(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var abc = new Date(1978,3); + [ abc.getYear(), abc.getMonth(), abc.valueOf() ]; + `, "78,3,260236800000") + }) +} + +func TestDate_setMilliseconds(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(); + def = abc.setMilliseconds(); + [ abc, def ]; + `, "Invalid Date,NaN") + }) +} + +func TestDate_new(t *testing.T) { + // FIXME? + // This is probably incorrect, due to differences in Go date/time handling + // versus ECMA date/time handling, but we'll leave this here for + // future reference + return + + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + [ + new Date(1899, 11).valueOf(), + new Date(1899, 12).valueOf(), + new Date(1900, 0).valueOf() + ] + `, "-2211638400000,-2208960000000,-2208960000000") + }) +} + +func TestDateComparison(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var now0 = Date.now(); + var now1 = (new Date()).toString(); + [ now0 === now1, Math.abs(now0 - Date.parse(now1)) <= 1000 ]; + `, "false,true") + }) +} + +func TestDate_setSeconds(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setSeconds(10, 12); + + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCSeconds(10, 12); + + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setSeconds.length`, 2) + test(`Date.prototype.setUTCSeconds.length`, 2) + }) +} + +func TestDate_setMinutes(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setMinutes(8, 10, 12); + + def.setMinutes(8); + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCMinutes(8, 10, 12); + + def.setUTCMinutes(8); + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setMinutes.length`, 3) + test(`Date.prototype.setUTCMinutes.length`, 3) + }) +} + +func TestDate_setHours(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setHours(6, 8, 10, 12); + + def.setHours(6); + def.setMinutes(8); + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCHours(6, 8, 10, 12); + + def.setUTCHours(6); + def.setUTCMinutes(8); + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setHours.length`, 4) + test(`Date.prototype.setUTCHours.length`, 4) + }) +} + +func TestDate_setMonth(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setMonth(6, 8); + + def.setMonth(6); + def.setDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCMonth(6, 8); + + def.setUTCMonth(6); + def.setUTCDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setMonth.length`, 2) + test(`Date.prototype.setUTCMonth.length`, 2) + }) +} + +func TestDate_setFullYear(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setFullYear(1981, 6, 8); + + def.setFullYear(1981); + def.setMonth(6); + def.setDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCFullYear(1981, 6, 8); + + def.setUTCFullYear(1981); + def.setUTCMonth(6); + def.setUTCDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setFullYear.length`, 3) + test(`Date.prototype.setUTCFullYear.length`, 3) + }) +} + +func TestDate_setTime(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var abc = new Date(1999, 6, 1); + var def = new Date(); + def.setTime(abc.getTime()); + [ def, abc.valueOf() == def.valueOf() ]; + `, "Thu, 01 Jul 1999 00:00:00 UTC,true") + + test(`Date.prototype.setTime.length`, 1) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/dbg.go b/Godeps/_workspace/src/github.com/obscuren/otto/dbg.go new file mode 100644 index 000000000..51fbdc206 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/dbg.go @@ -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() diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/dbg/dbg.go b/Godeps/_workspace/src/github.com/obscuren/otto/dbg/dbg.go new file mode 100644 index 000000000..83bf6c573 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/dbg/dbg.go @@ -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...) + } + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/documentation_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/documentation_test.go new file mode 100644 index 000000000..dca5093f7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/documentation_test.go @@ -0,0 +1,95 @@ +package otto + +import ( + "fmt" +) + +func ExampleSynopsis() { + + vm := New() + vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 + `) + + value, _ := vm.Get("abc") + { + value, _ := value.ToInteger() + fmt.Println(value) + } + + vm.Set("def", 11) + vm.Run(` + console.log("The value of def is " + def); + `) + + vm.Set("xyzzy", "Nothing happens.") + vm.Run(` + console.log(xyzzy.length); + `) + + value, _ = vm.Run("xyzzy.length") + { + value, _ := value.ToInteger() + fmt.Println(value) + } + + value, err := vm.Run("abcdefghijlmnopqrstuvwxyz.length") + fmt.Println(value) + fmt.Println(err) + + vm.Set("sayHello", func(call FunctionCall) Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return UndefinedValue() + }) + + vm.Set("twoPlus", func(call FunctionCall) Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result + }) + + value, _ = vm.Run(` + sayHello("Xyzzy"); + sayHello(); + + result = twoPlus(2.0); + `) + fmt.Println(value) + + // Output: + // The value of abc is 4 + // 4 + // The value of def is 11 + // 16 + // 16 + // undefined + // ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined + // Hello, Xyzzy. + // Hello, undefined. + // 4 +} + +func ExampleConsole() { + + vm := New() + console := map[string]interface{}{ + "log": func(call FunctionCall) Value { + fmt.Println("console.log:", formatForConsole(call.ArgumentList)) + return UndefinedValue() + }, + } + + err := vm.Set("console", console) + + value, err := vm.Run(` + console.log("Hello, World."); + `) + fmt.Println(value) + fmt.Println(err) + + // Output: + // console.log: Hello, World. + // undefined + // +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/environment.go b/Godeps/_workspace/src/github.com/obscuren/otto/environment.go new file mode 100644 index 000000000..891a3c9af --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/environment.go @@ -0,0 +1,280 @@ +package otto + +import ( + "fmt" +) + +// _environment + +type _environment interface { + HasBinding(string) bool + + CreateMutableBinding(string, bool) + SetMutableBinding(string, Value, bool) + // SetMutableBinding with Lazy CreateMutableBinding(..., true) + SetValue(string, Value, bool) + + GetBindingValue(string, bool) Value + GetValue(string, bool) Value // GetBindingValue + DeleteBinding(string) bool + ImplicitThisValue() *_object + + Outer() _environment + + newReference(string, bool) _reference + clone(clone *_clone) _environment + runtimeOf() *_runtime +} + +// _functionEnvironment + +type _functionEnvironment struct { + _declarativeEnvironment + arguments *_object + indexOfArgumentName map[string]string +} + +func (runtime *_runtime) newFunctionEnvironment(outer _environment) *_functionEnvironment { + return &_functionEnvironment{ + _declarativeEnvironment: _declarativeEnvironment{ + runtime: runtime, + outer: outer, + property: map[string]_declarativeProperty{}, + }, + } +} + +func (self0 _functionEnvironment) clone(clone *_clone) _environment { + return &_functionEnvironment{ + *(self0._declarativeEnvironment.clone(clone).(*_declarativeEnvironment)), + clone.object(self0.arguments), + self0.indexOfArgumentName, + } +} + +func (self _functionEnvironment) runtimeOf() *_runtime { + return self._declarativeEnvironment.runtimeOf() +} + +// _objectEnvironment + +type _objectEnvironment struct { + runtime *_runtime + outer _environment + Object *_object + ProvideThis bool +} + +func (self *_objectEnvironment) runtimeOf() *_runtime { + return self.runtime +} + +func (runtime *_runtime) newObjectEnvironment(object *_object, outer _environment) *_objectEnvironment { + if object == nil { + object = runtime.newBaseObject() + object.class = "environment" + } + return &_objectEnvironment{ + runtime: runtime, + outer: outer, + Object: object, + } +} + +func (self0 *_objectEnvironment) clone(clone *_clone) _environment { + self1, exists := clone.objectEnvironment(self0) + if exists { + return self1 + } + *self1 = _objectEnvironment{ + clone.runtime, + clone.environment(self0.outer), + clone.object(self0.Object), + self0.ProvideThis, + } + return self1 +} + +func (self *_objectEnvironment) HasBinding(name string) bool { + return self.Object.hasProperty(name) +} + +func (self *_objectEnvironment) CreateMutableBinding(name string, deletable bool) { + if self.Object.hasProperty(name) { + panic(hereBeDragons()) + } + mode := _propertyMode(0111) + if !deletable { + mode = _propertyMode(0110) + } + // TODO False? + self.Object.defineProperty(name, UndefinedValue(), mode, false) +} + +func (self *_objectEnvironment) SetMutableBinding(name string, value Value, strict bool) { + self.Object.put(name, value, strict) +} + +func (self *_objectEnvironment) SetValue(name string, value Value, throw bool) { + if !self.HasBinding(name) { + self.CreateMutableBinding(name, true) // Configurable by default + } + self.SetMutableBinding(name, value, throw) +} + +func (self *_objectEnvironment) GetBindingValue(name string, strict bool) Value { + if self.Object.hasProperty(name) { + return self.Object.get(name) + } + if strict { + panic(newReferenceError("Not Defined", name)) + } + return UndefinedValue() +} + +func (self *_objectEnvironment) GetValue(name string, throw bool) Value { + return self.GetBindingValue(name, throw) +} + +func (self *_objectEnvironment) DeleteBinding(name string) bool { + return self.Object.delete(name, false) +} + +func (self *_objectEnvironment) ImplicitThisValue() *_object { + if self.ProvideThis { + return self.Object + } + return nil +} + +func (self *_objectEnvironment) Outer() _environment { + return self.outer +} + +func (self *_objectEnvironment) newReference(name string, strict bool) _reference { + return newPropertyReference(self.Object, name, strict) +} + +// _declarativeEnvironment + +func (runtime *_runtime) newDeclarativeEnvironment(outer _environment) *_declarativeEnvironment { + return &_declarativeEnvironment{ + runtime: runtime, + outer: outer, + property: map[string]_declarativeProperty{}, + } +} + +func (self0 *_declarativeEnvironment) clone(clone *_clone) _environment { + self1, exists := clone.declarativeEnvironment(self0) + if exists { + return self1 + } + property := make(map[string]_declarativeProperty, len(self0.property)) + for index, value := range self0.property { + property[index] = clone.declarativeProperty(value) + } + *self1 = _declarativeEnvironment{ + clone.runtime, + clone.environment(self0.outer), + property, + } + return self1 +} + +type _declarativeProperty struct { + value Value + mutable bool + deletable bool + readable bool +} + +type _declarativeEnvironment struct { + runtime *_runtime + outer _environment + property map[string]_declarativeProperty +} + +func (self *_declarativeEnvironment) HasBinding(name string) bool { + _, exists := self.property[name] + return exists +} + +func (self *_declarativeEnvironment) runtimeOf() *_runtime { + return self.runtime +} + +func (self *_declarativeEnvironment) CreateMutableBinding(name string, deletable bool) { + _, exists := self.property[name] + if exists { + panic(fmt.Errorf("CreateMutableBinding: %s: already exists", name)) + } + self.property[name] = _declarativeProperty{ + value: UndefinedValue(), + mutable: true, + deletable: deletable, + readable: false, + } +} + +func (self *_declarativeEnvironment) SetMutableBinding(name string, value Value, strict bool) { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("SetMutableBinding: %s: missing", name)) + } + if property.mutable { + property.value = value + self.property[name] = property + } else { + typeErrorResult(strict) + } +} + +func (self *_declarativeEnvironment) SetValue(name string, value Value, throw bool) { + if !self.HasBinding(name) { + self.CreateMutableBinding(name, false) // NOT deletable by default + } + self.SetMutableBinding(name, value, throw) +} + +func (self *_declarativeEnvironment) GetBindingValue(name string, strict bool) Value { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("GetBindingValue: %s: missing", name)) + } + if !property.mutable && !property.readable { + if strict { + panic(newTypeError()) + } + return UndefinedValue() + } + return property.value +} + +func (self *_declarativeEnvironment) GetValue(name string, throw bool) Value { + return self.GetBindingValue(name, throw) +} + +func (self *_declarativeEnvironment) 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 *_declarativeEnvironment) ImplicitThisValue() *_object { + return nil +} + +func (self *_declarativeEnvironment) Outer() _environment { + return self.outer +} + +func (self *_declarativeEnvironment) newReference(name string, strict bool) _reference { + return newEnvironmentReference(self, name, strict, nil) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/error.go b/Godeps/_workspace/src/github.com/obscuren/otto/error.go new file mode 100644 index 000000000..887b954bd --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/error.go @@ -0,0 +1,152 @@ +package otto + +import ( + "errors" + "fmt" + + "github.com/robertkrimen/otto/ast" +) + +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 + + Line int // Hackish -- line where the error/exception occurred +} + +var messageDetail map[string]string = map[string]string{ + "notDefined": "%v is not defined", +} + +func messageFromDescription(description string, argumentList ...interface{}) string { + message := messageDetail[description] + if message == "" { + message = description + } + message = fmt.Sprintf(message, argumentList...) + return message +} + +func (self _error) MessageValue() Value { + if self.Message == "" { + return UndefinedValue() + } + return toValue_string(self.Message) +} + +func (self _error) String() string { + if len(self.Name) == 0 { + return self.Message + } + if len(self.Message) == 0 { + return self.Name + } + return fmt.Sprintf("%s: %s", self.Name, self.Message) +} + +func newError(name string, argumentList ...interface{}) _error { + description := "" + var node ast.Node = nil + length := len(argumentList) + if length > 0 { + if node, _ = argumentList[length-1].(ast.Node); node != nil || argumentList[length-1] == nil { + argumentList = argumentList[0 : length-1] + length -= 1 + } + if length > 0 { + description, argumentList = argumentList[0].(string), argumentList[1:] + } + } + return _error{ + Name: name, + Message: messageFromDescription(description, argumentList...), + Line: -1, + } + //error := _error{ + // Name: name, + // Message: messageFromDescription(description, argumentList...), + // Line: -1, + //} + //if node != nil { + // error.Line = ast.position() + //} + //return error +} + +func newReferenceError(argumentList ...interface{}) _error { + return newError("ReferenceError", argumentList...) +} + +func newTypeError(argumentList ...interface{}) _error { + return newError("TypeError", argumentList...) +} + +func newRangeError(argumentList ...interface{}) _error { + return newError("RangeError", argumentList...) +} + +func newSyntaxError(argumentList ...interface{}) _error { + return newError("SyntaxError", argumentList...) +} + +func newURIError(argumentList ...interface{}) _error { + return newError("URIError", argumentList...) +} + +func typeErrorResult(throw bool) bool { + if throw { + panic(newTypeError()) + } + return false +} + +func catchPanic(function func()) (err error) { + // FIXME + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + switch caught := caught.(type) { + //case *_syntaxError: + // err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+0)) + // return + case _error: + if caught.Line == -1 { + err = errors.New(caught.String()) + } else { + // We're 0-based (for now), hence the + 1 + err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+1)) + } + return + case Value: + err = errors.New(toString(caught)) + return + //case string: + // if strings.HasPrefix(caught, "SyntaxError:") { + // err = errors.New(caught) + // return + // } + } + panic(caught) + } + }() + function() + return nil +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/error_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/error_test.go new file mode 100644 index 000000000..d0580c618 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/error_test.go @@ -0,0 +1,62 @@ +package otto + +import ( + "testing" +) + +func TestError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ]; + `, "Error,,true") + }) +} + +func TestError_instanceof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`(new TypeError()) instanceof Error`, true) + }) +} + +func TestPanicValue(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + value, err := call.Otto.Run(`({ def: 3.14159 })`) + is(err, nil) + panic(value) + }) + + test(` + try { + abc(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def ]; + `, "false,,3.14159") + }) +} + +func Test_catchPanic(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(` + A syntax error that + does not define + var; + abc; + `) + is(err, "!=", nil) + + _, err = vm.Call(`abc.def`, nil) + is(err, "!=", nil) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/evaluate.go b/Godeps/_workspace/src/github.com/obscuren/otto/evaluate.go new file mode 100644 index 000000000..d8c74c0f6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/evaluate.go @@ -0,0 +1,317 @@ +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 UndefinedValue() +} + +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 UndefinedValue() +} + +func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value { + + leftValue := self.GetValue(left) + + switch operator { + + // Additive + case token.PLUS: + leftValue = toPrimitive(leftValue) + rightValue := self.GetValue(right) + rightValue = toPrimitive(rightValue) + + if leftValue.IsString() || rightValue.IsString() { + return toValue_string(strings.Join([]string{leftValue.toString(), rightValue.toString()}, "")) + } else { + return toValue_float64(leftValue.toFloat() + rightValue.toFloat()) + } + case token.MINUS: + rightValue := self.GetValue(right) + return toValue_float64(leftValue.toFloat() - rightValue.toFloat()) + + // Multiplicative + case token.MULTIPLY: + rightValue := self.GetValue(right) + return toValue_float64(leftValue.toFloat() * rightValue.toFloat()) + case token.SLASH: + rightValue := self.GetValue(right) + return self.evaluateDivide(leftValue.toFloat(), rightValue.toFloat()) + case token.REMAINDER: + rightValue := self.GetValue(right) + return toValue_float64(math.Mod(leftValue.toFloat(), rightValue.toFloat())) + + // Logical + case token.LOGICAL_AND: + left := toBoolean(leftValue) + if !left { + return FalseValue() + } + return toValue_bool(toBoolean(self.GetValue(right))) + case token.LOGICAL_OR: + left := toBoolean(leftValue) + if left { + return TrueValue() + } + return toValue_bool(toBoolean(self.GetValue(right))) + + // Bitwise + case token.AND: + rightValue := self.GetValue(right) + return toValue_int32(toInt32(leftValue) & toInt32(rightValue)) + case token.OR: + rightValue := self.GetValue(right) + return toValue_int32(toInt32(leftValue) | toInt32(rightValue)) + case token.EXCLUSIVE_OR: + rightValue := self.GetValue(right) + 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 := self.GetValue(right) + return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f)) + case token.SHIFT_RIGHT: + rightValue := self.GetValue(right) + return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f)) + case token.UNSIGNED_SHIFT_RIGHT: + rightValue := self.GetValue(right) + // Shifting an unsigned integer is a logical shift + return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f)) + + case token.INSTANCEOF: + rightValue := self.GetValue(right) + if !rightValue.IsObject() { + panic(newTypeError("Expecting a function in instanceof check, but got: %v", rightValue)) + } + return toValue_bool(rightValue._object().HasInstance(leftValue)) + + case token.IN: + rightValue := self.GetValue(right) + if !rightValue.IsObject() { + panic(newTypeError()) + } + return toValue_bool(rightValue._object().hasProperty(toString(leftValue))) + } + + panic(hereBeDragons(operator)) +} + +func valueKindDispatchKey(left _valueType, right _valueType) 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.toFloat() == y.toFloat() }, + key(valueString, valueObject): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, + key(valueObject, valueNumber): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, + key(valueObject, valueString): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, + } +} + +type _lessThanResult int + +const ( + lessThanFalse _lessThanResult = iota + lessThanTrue + lessThanUndefined +) + +func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult { + + x := UndefinedValue() + y := x + + if leftFirst { + x = toNumberPrimitive(left) + y = toNumberPrimitive(right) + } else { + y = toNumberPrimitive(right) + x = toNumberPrimitive(left) + } + + result := false + if x._valueType != valueString || y._valueType != valueString { + x, y := x.toFloat(), y.toFloat() + if math.IsNaN(x) || math.IsNaN(y) { + return lessThanUndefined + } + result = x < y + } else { + x, y := x.toString(), y.toString() + result = x < y + } + + if result { + return lessThanTrue + } + + return lessThanFalse +} + +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 := self.GetValue(left) + y := self.GetValue(right) + + 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._valueType != y._valueType { + result = false + } else { + kindEqualKind = true + } + case token.NOT_EQUAL: + negate = true + fallthrough + case token.EQUAL: + if x._valueType == y._valueType { + kindEqualKind = true + } else if x._valueType <= valueUndefined && y._valueType <= valueUndefined { + result = true + } else if x._valueType <= valueUndefined || y._valueType <= valueUndefined { + result = false + } else if x._valueType <= valueString && y._valueType <= valueString { + result = x.toFloat() == y.toFloat() + } else if x._valueType == valueBoolean { + result = self.calculateComparison(token.EQUAL, toValue_float64(x.toFloat()), y) + } else if y._valueType == valueBoolean { + result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.toFloat())) + } else if x._valueType == valueObject { + result = self.calculateComparison(token.EQUAL, toPrimitive(x), y) + } else if y._valueType == 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._valueType { + case valueUndefined, valueNull: + result = true + case valueNumber: + x := x.toFloat() + y := y.toFloat() + if math.IsNaN(x) || math.IsNaN(y) { + result = false + } else { + result = x == y + } + case valueString: + result = x.toString() == y.toString() + case valueBoolean: + result = x.toBoolean() == y.toBoolean() + 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._valueType, comparator, y, y._valueType)) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/execution_context.go b/Godeps/_workspace/src/github.com/obscuren/otto/execution_context.go new file mode 100644 index 000000000..07d891022 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/execution_context.go @@ -0,0 +1,40 @@ +package otto + +type _executionContext struct { + LexicalEnvironment _environment + VariableEnvironment _environment + this *_object + eval bool // Replace this with kind? +} + +func newExecutionContext(lexical _environment, variable _environment, this *_object) *_executionContext { + return &_executionContext{ + LexicalEnvironment: lexical, + VariableEnvironment: variable, + this: this, + } +} + +func (self *_executionContext) getValue(name string) Value { + strict := false + return self.LexicalEnvironment.GetValue(name, strict) +} + +func (self *_executionContext) setValue(name string, value Value, throw bool) { + self.LexicalEnvironment.SetValue(name, value, throw) +} + +func (self *_executionContext) newLexicalEnvironment(object *_object) (_environment, *_objectEnvironment) { + // Get runtime from the object (for now) + runtime := object.runtime + previousLexical := self.LexicalEnvironment + newLexical := runtime.newObjectEnvironment(object, self.LexicalEnvironment) + self.LexicalEnvironment = newLexical + return previousLexical, newLexical +} + +func (self *_executionContext) newDeclarativeEnvironment(runtime *_runtime) _environment { + previousLexical := self.LexicalEnvironment + self.LexicalEnvironment = runtime.newDeclarativeEnvironment(self.LexicalEnvironment) + return previousLexical +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/file/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/file/README.markdown new file mode 100644 index 000000000..72bbdb1fd --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/file/README.markdown @@ -0,0 +1,72 @@ +# file +-- + import "github.com/robertkrimen/otto/file" + +Package file encapsulates the file abstractions used by the ast & parser. + +## Usage + +#### 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) 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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/file/file.go b/Godeps/_workspace/src/github.com/obscuren/otto/file/file.go new file mode 100644 index 000000000..5e893d819 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/file/file.go @@ -0,0 +1,106 @@ +// Package file encapsulates the file abstractions used by the ast & parser. +// +package file + +import ( + "fmt" + "strings" +) + +// 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{ + filename: 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 +} + +// Position converts an Idx in the FileSet into a Position. +func (self *FileSet) Position(idx Idx) *Position { + position := &Position{} + for _, file := range self.files { + if idx <= Idx(file.base+len(file.src)) { + offset := int(idx) - file.base + src := file.src[:offset] + position.Filename = file.filename + position.Offset = offset + position.Line = 1 + strings.Count(src, "\n") + if index := strings.LastIndex(src, "\n"); index >= 0 { + position.Column = offset - index + } else { + position.Column = 1 + len(src) + } + } + } + return position +} + +type _file struct { + filename string + src string + base int // This will always be 1 or greater +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/function_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/function_test.go new file mode 100644 index 000000000..bd438022d --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/function_test.go @@ -0,0 +1,272 @@ +package otto + +import ( + "testing" +) + +func TestFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = Object.getOwnPropertyDescriptor(Function, "prototype"); + [ [ typeof Function.prototype, typeof Function.prototype.length, Function.prototype.length ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "function,number,0,false,false,false") + }) +} + +func Test_argumentList2parameterList(t *testing.T) { + tt(t, func() { + is(argumentList2parameterList([]Value{toValue("abc, def"), toValue("ghi")}), []string{"abc", "def", "ghi"}) + }) +} + +func TestFunction_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + new Function({}); + `, "SyntaxError: Unexpected identifier") + + test(` + var abc = Function("def, ghi", "jkl", "return def+ghi+jkl"); + [ typeof abc, abc instanceof Function, abc("ab", "ba", 1) ]; + `, "function,true,abba1") + + test(`raise: + var abc = { + toString: function() { throw 1; } + }; + var def = { + toString: function() { throw 2; } + }; + var ghi = new Function(abc, def); + ghi; + `, "1") + + // S15.3.2.1_A3_T10 + test(`raise: + var abc = { + toString: function() { return "z;x"; } + }; + var def = "return this"; + var ghi = new Function(abc, def); + ghi; + `, "SyntaxError: Unexpected token ;") + + test(`raise: + var abc; + var def = "return true"; + var ghi = new Function(null, def); + ghi; + `, "SyntaxError: Unexpected token null") + }) +} + +func TestFunction_apply(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Function.prototype.apply.length`, 2) + test(`String.prototype.substring.apply("abc", [1, 11])`, "bc") + }) +} + +func TestFunction_call(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Function.prototype.call.length`, 1) + test(`String.prototype.substring.call("abc", 1, 11)`, "bc") + }) +} + +func TestFunctionArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Should not be able to delete arguments + test(` + function abc(def, arguments){ + delete def; + return def; + } + abc(1); + `, 1) + + // Again, should not be able to delete arguments + test(` + function abc(def){ + delete def; + return def; + } + abc(1); + `, 1) + + // Test typeof of a function argument + test(` + function abc(def, ghi, jkl){ + return typeof jkl + } + abc("1st", "2nd", "3rd", "4th", "5th"); + `, "string") + + test(` + function abc(def, ghi, jkl){ + arguments[0] = 3.14; + arguments[1] = 'Nothing happens'; + arguments[2] = 42; + if (3.14 === def && 'Nothing happens' === ghi && 42 === jkl) + return true; + } + abc(-1, 4.2, 314); + `, true) + }) +} + +func TestFunctionDeclarationInFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Function declarations happen AFTER parameter/argument declarations + // That is, a function declared within a function will shadow/overwrite + // declared parameters + + test(` + function abc(def){ + return def; + function def(){ + return 1; + } + } + typeof abc(); + `, "function") + }) +} + +func TestArguments_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + var def = true; + var ghi = {}; + (function (a, b, c) { + Object.defineProperty(arguments, "0", { + value: 42, + writable: false, + enumerable: false, + configurable: false + }); + Object.defineProperty(arguments, "1", { + value: 3.14, + configurable: true, + enumerable: true + }); + abc = Object.getOwnPropertyDescriptor(arguments, "0"); + for (var name in arguments) { + ghi[name] = (ghi[name] || 0) + 1; + if (name === "0") { + def = false; + } + } + }(0, 1, 2)); + [ abc.value, abc.writable, abc.enumerable, abc.configurable, def, ghi["1"] ]; + `, "42,false,false,false,true,1") + }) +} + +func TestFunction_bind(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = function(){ + return "abc"; + }; + def = abc.bind(); + [ typeof def.prototype, typeof def.hasOwnProperty, def.hasOwnProperty("caller"), def.hasOwnProperty("arguments"), def() ]; + `, "object,function,true,true,abc") + + test(` + abc = function(){ + return arguments[1]; + }; + def = abc.bind(undefined, "abc"); + ghi = abc.bind(undefined, "abc", "ghi"); + [ def(), def("def"), ghi("def") ]; + `, ",def,ghi") + + test(` + var abc = function () {}; + var ghi; + try { + Object.defineProperty(Function.prototype, "xyzzy", { + value: 1001, + writable: true, + enumerable: true, + configurable: true + }); + var def = abc.bind({}); + ghi = !def.hasOwnProperty("xyzzy") && ghi.xyzzy === 1001; + } finally { + delete Function.prototype.xyzzy; + } + [ ghi ]; + `, "true") + + test(` + var abc = function (def, ghi) {}; + var jkl = abc.bind({}); + var mno = abc.bind({}, 1, 2); + [ jkl.length, mno.length ]; + `, "2,0") + + test(`raise: + Math.bind(); + `, "TypeError: undefined is not a function") + + test(` + function construct(fn, arguments) { + var bound = Function.prototype.bind.apply(fn, [null].concat(arguments)); + return new bound(); + } + var abc = construct(Date, [1957, 4, 27]); + Object.prototype.toString.call(abc); + `, "[object Date]") + + test(` + var fn = function (x, y, z) { + var result = {}; + result.abc = x + y + z; + result.def = arguments[0] === "a" && arguments.length === 3; + return result; + }; + var newFn = Function.prototype.bind.call(fn, {}, "a", "b", "c"); + var result = new newFn(); + [ result.hasOwnProperty("abc"), result.hasOwnProperty("def"), result.abc, result.def ]; + `, "true,true,abc,true") + }) +} + +func TestFunction_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Function.prototype.toString.call(undefined); + `, "TypeError") + + test(` + abc = function() { return -1 ; +} + 1; + abc.toString(); + `, "function() { return -1 ;\n}") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/global.go b/Godeps/_workspace/src/github.com/obscuren/otto/global.go new file mode 100644 index 000000000..87030a0e0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/global.go @@ -0,0 +1,214 @@ +package otto + +import ( + "strconv" + "time" +) + +var ( + prototypeValueObject = interface{}(nil) + prototypeValueFunction = _functionObject{ + call: _nativeCallFunction{"", func(_ FunctionCall) Value { + return UndefinedValue() + }}, + } + prototypeValueString = _stringASCII("") + // TODO Make this just false? + prototypeValueBoolean = Value{ + _valueType: valueBoolean, + value: false, + } + prototypeValueNumber = Value{ + _valueType: valueNumber, + value: 0, + } + prototypeValueDate = _dateObject{ + epoch: 0, + isNaN: false, + time: time.Unix(0, 0).UTC(), + value: Value{ + _valueType: valueNumber, + value: 0, + }, + } + prototypeValueRegExp = _regExpObject{ + regularExpression: nil, + global: false, + ignoreCase: false, + multiline: false, + source: "", + flags: "", + } +) + +func newContext() *_runtime { + + self := &_runtime{} + + self.GlobalEnvironment = self.newObjectEnvironment(nil, nil) + self.GlobalObject = self.GlobalEnvironment.Object + + self.EnterGlobalExecutionContext() + + _newContext(self) + + self.eval = self.GlobalObject.property["eval"].value.(Value).value.(*_object) + self.GlobalObject.prototype = self.Global.ObjectPrototype + //self.parser = ast.NewParser() + + 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(newTypeError("Cannot supply flags when constructing one RegExp from another")) + } + regExp := object.regExpValue() + pattern = regExp.source + flags = regExp.flags + } else { + if patternValue.IsDefined() { + pattern = toString(patternValue) + } + if flagsValue.IsDefined() { + flags = toString(flagsValue) + } + } + + 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) *_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(message) + self.prototype = runtime.Global.ErrorPrototype + if name != "" { + self.defineProperty("name", toValue_string(name), 0111, false) + } + return self +} + +func (runtime *_runtime) newNativeFunction(name string, _nativeFunction _nativeFunction) *_object { + self := runtime.newNativeFunctionObject(name, _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 _environment) *_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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/global_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/global_test.go new file mode 100644 index 000000000..c5204e54c --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/global_test.go @@ -0,0 +1,352 @@ +package otto + +import ( + "fmt" + "math" + "strings" + "testing" +) + +func TestGlobal(t *testing.T) { + tt(t, func() { + test, vm := test() + + runtime := vm.vm.runtime + + { + call := func(object interface{}, src string, argumentList ...interface{}) Value { + var tgt *Object + switch object := object.(type) { + case Value: + tgt = object.Object() + case *Object: + tgt = object + case *_object: + tgt = toValue_object(object).Object() + default: + panic("Here be dragons.") + } + value, err := tgt.Call(src, argumentList...) + is(err, nil) + return value + } + + value := runtime.localGet("Object")._object().Call(UndefinedValue(), []Value{toValue(runtime.newObject())}) + is(value.IsObject(), true) + is(value, "[object Object]") + is(value._object().prototype == runtime.Global.ObjectPrototype, true) + is(value._object().prototype == runtime.Global.Object.get("prototype")._object(), true) + is(value._object().get("toString"), "function toString() { [native code] }") + is(call(value.Object(), "hasOwnProperty", "hasOwnProperty"), false) + + is(call(value._object().get("toString")._object().prototype, "toString"), "function () { [native code] }") // TODO Is this right? + is(value._object().get("toString")._object().get("toString"), "function toString() { [native code] }") + is(value._object().get("toString")._object().get("toString")._object(), "function toString() { [native code] }") + + is(call(value._object(), "propertyIsEnumerable", "isPrototypeOf"), false) + value._object().put("xyzzy", toValue_string("Nothing happens."), false) + is(call(value, "propertyIsEnumerable", "isPrototypeOf"), false) + is(call(value, "propertyIsEnumerable", "xyzzy"), true) + is(value._object().get("xyzzy"), "Nothing happens.") + + is(call(runtime.localGet("Object"), "isPrototypeOf", value), false) + is(call(runtime.localGet("Object")._object().get("prototype"), "isPrototypeOf", value), true) + is(call(runtime.localGet("Function"), "isPrototypeOf", value), false) + + is(runtime.newObject().prototype == runtime.Global.Object.get("prototype")._object(), true) + + abc := runtime.newBoolean(toValue_bool(true)) + is(toValue_object(abc), "true") // TODO Call primitive? + + def := runtime.localGet("Boolean")._object().Construct(UndefinedValue(), []Value{}) + is(def, "false") // TODO Call primitive? + } + + test(`new Number().constructor == Number`, true) + + test(`this.hasOwnProperty`, "function hasOwnProperty() { [native code] }") + + test(`eval.length === 1`, true) + test(`eval.prototype === undefined`, true) + test(`raise: new eval()`, "TypeError: function eval() { [native code] } is not a constructor") + + test(` + [ + [ delete undefined, undefined ], + [ delete NaN, NaN ], + [ delete Infinity, Infinity ], + ]; + `, "false,,false,NaN,false,Infinity") + + test(` + Object.getOwnPropertyNames(Function('return this')()).sort(); + `, "Array,Boolean,Date,Error,EvalError,Function,Infinity,JSON,Math,NaN,Number,Object,RangeError,ReferenceError,RegExp,String,SyntaxError,TypeError,URIError,console,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,escape,eval,isFinite,isNaN,parseFloat,parseInt,undefined,unescape") + + // __defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf + test(` + Object.getOwnPropertyNames(Object.prototype).sort(); + `, "constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf") + + // arguments,caller,length,name,prototype + test(` + Object.getOwnPropertyNames(EvalError).sort(); + `, "length,prototype") + + test(` + var abc = []; + var def = [EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError]; + for (constructor in def) { + abc.push(def[constructor] === def[constructor].prototype.constructor); + } + def = [Array, Boolean, Date, Function, Number, Object, RegExp, String, SyntaxError]; + for (constructor in def) { + abc.push(def[constructor] === def[constructor].prototype.constructor); + } + abc; + `, "true,true,true,true,true,true,true,true,true,true,true,true,true,true,true") + + test(` + [ Array.prototype.constructor === Array, Array.constructor === Function ]; + `, "true,true") + + test(` + [ Number.prototype.constructor === Number, Number.constructor === Function ]; + `, "true,true") + + test(` + [ Function.prototype.constructor === Function, Function.constructor === Function ]; + `, "true,true") + }) +} + +func TestGlobalLength(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ Object.length, Function.length, RegExp.length, Math.length ]; + `, "1,1,2,") + }) +} + +func TestGlobalError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ TypeError.length, TypeError(), TypeError("Nothing happens.") ]; + `, "1,TypeError,TypeError: Nothing happens.") + + test(` + [ URIError.length, URIError(), URIError("Nothing happens.") ]; + `, "1,URIError,URIError: Nothing happens.") + }) +} + +func TestGlobalReadOnly(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Number.POSITIVE_INFINITY`, math.Inf(1)) + + test(` + Number.POSITIVE_INFINITY = 1; + `, 1) + + test(`Number.POSITIVE_INFINITY`, math.Inf(1)) + + test(` + Number.POSITIVE_INFINITY = 1; + Number.POSITIVE_INFINITY; + `, math.Inf(1)) + }) +} + +func Test_isNaN(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`isNaN(0)`, false) + test(`isNaN("Xyzzy")`, true) + test(`isNaN()`, true) + test(`isNaN(NaN)`, true) + test(`isNaN(Infinity)`, false) + + test(`isNaN.length === 1`, true) + test(`isNaN.prototype === undefined`, true) + }) +} + +func Test_isFinite(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`isFinite(0)`, true) + test(`isFinite("Xyzzy")`, false) + test(`isFinite()`, false) + test(`isFinite(NaN)`, false) + test(`isFinite(Infinity)`, false) + test(`isFinite(new Number(451));`, true) + + test(`isFinite.length === 1`, true) + test(`isFinite.prototype === undefined`, true) + }) +} + +func Test_parseInt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`parseInt("0")`, 0) + test(`parseInt("11")`, 11) + test(`parseInt(" 11")`, 11) + test(`parseInt("11 ")`, 11) + test(`parseInt(" 11 ")`, 11) + test(`parseInt(" 11\n")`, 11) + test(`parseInt(" 11\n", 16)`, 17) + + test(`parseInt("Xyzzy")`, _NaN) + + test(`parseInt(" 0x11\n", 16)`, 17) + test(`parseInt("0x0aXyzzy", 16)`, 10) + test(`parseInt("0x1", 0)`, 1) + test(`parseInt("0x10000000000000000000", 16)`, float64(75557863725914323419136)) + + test(`parseInt.length === 2`, true) + test(`parseInt.prototype === undefined`, true) + }) +} + +func Test_parseFloat(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`parseFloat("0")`, 0) + test(`parseFloat("11")`, 11) + test(`parseFloat(" 11")`, 11) + test(`parseFloat("11 ")`, 11) + test(`parseFloat(" 11 ")`, 11) + test(`parseFloat(" 11\n")`, 11) + test(`parseFloat(" 11\n", 16)`, 11) + test(`parseFloat("11.1")`, 11.1) + + test(`parseFloat("Xyzzy")`, _NaN) + + test(`parseFloat(" 0x11\n", 16)`, 0) + test(`parseFloat("0x0a")`, 0) + test(`parseFloat("0x0aXyzzy")`, 0) + test(`parseFloat("Infinity")`, _Infinity) + test(`parseFloat("infinity")`, _NaN) + test(`parseFloat("0x")`, 0) + test(`parseFloat("11x")`, 11) + test(`parseFloat("Infinity1")`, _Infinity) + + test(`parseFloat.length === 1`, true) + test(`parseFloat.prototype === undefined`, true) + }) +} + +func Test_encodeURI(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`encodeURI("http://example.com/ Nothing happens.")`, "http://example.com/%20Nothing%20happens.") + test(`encodeURI("http://example.com/ _^#")`, "http://example.com/%20_%5E#") + test(`encodeURI(String.fromCharCode("0xE000"))`, "%EE%80%80") + test(`encodeURI(String.fromCharCode("0xFFFD"))`, "%EF%BF%BD") + test(`raise: encodeURI(String.fromCharCode("0xDC00"))`, "URIError: URI malformed") + + test(`encodeURI.length === 1`, true) + test(`encodeURI.prototype === undefined`, true) + }) +} + +func Test_encodeURIComponent(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`encodeURIComponent("http://example.com/ Nothing happens.")`, "http%3A%2F%2Fexample.com%2F%20Nothing%20happens.") + test(`encodeURIComponent("http://example.com/ _^#")`, "http%3A%2F%2Fexample.com%2F%20_%5E%23") + }) +} + +func Test_decodeURI(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`decodeURI(encodeURI("http://example.com/ Nothing happens."))`, "http://example.com/ Nothing happens.") + test(`decodeURI(encodeURI("http://example.com/ _^#"))`, "http://example.com/ _^#") + test(`raise: decodeURI("http://example.com/ _^#%")`, "URIError: URI malformed") + test(`raise: decodeURI("%DF%7F")`, "URIError: URI malformed") + for _, check := range strings.Fields("+ %3B %2F %3F %3A %40 %26 %3D %2B %24 %2C %23") { + test(fmt.Sprintf(`decodeURI("%s")`, check), check) + } + + test(`decodeURI.length === 1`, true) + test(`decodeURI.prototype === undefined`, true) + }) +} + +func Test_decodeURIComponent(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`decodeURIComponent(encodeURI("http://example.com/ Nothing happens."))`, "http://example.com/ Nothing happens.") + test(`decodeURIComponent(encodeURI("http://example.com/ _^#"))`, "http://example.com/ _^#") + + test(`decodeURIComponent.length === 1`, true) + test(`decodeURIComponent.prototype === undefined`, true) + + test(` + var global = Function('return this')(); + var abc = Object.getOwnPropertyDescriptor(global, "decodeURIComponent"); + [ abc.value === global.decodeURIComponent, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + }) +} + +func TestGlobal_skipEnumeration(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var found = []; + for (var test in this) { + if (false || + test === 'NaN' || + test === 'undefined' || + test === 'Infinity' || + false) { + found.push(test) + } + } + found.length; + `, 0) + + test(` + var found = []; + for (var test in this) { + if (false || + test === 'Object' || + test === 'Function' || + test === 'String' || + test === 'Number' || + test === 'Array' || + test === 'Boolean' || + test === 'Date' || + test === 'RegExp' || + test === 'Error' || + test === 'EvalError' || + test === 'RangeError' || + test === 'ReferenceError' || + test === 'SyntaxError' || + test === 'TypeError' || + test === 'URIError' || + false) { + found.push(test) + } + } + found.length; + `, 0) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/inline b/Godeps/_workspace/src/github.com/obscuren/otto/inline new file mode 100644 index 000000000..a16d87ad7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/inline @@ -0,0 +1,1066 @@ +#!/usr/bin/env perl + +my $_fmt; +$_fmt = "gofmt"; +$_fmt = "cat -n" if "cat" eq ($ARGV[0] || ""); + +use strict; +use warnings; +use IO::File; + +my $self = __PACKAGE__; + +sub functionLabel ($) { + return "$_[0]_function"; +} + +sub trim ($) { + local $_ = shift; + s/^\s*//, s/\s*$// for $_; + return $_; +} + +open my $fmt, "|-", "$_fmt" or die $!; + +$fmt->print(<<_END_); +package otto + +import ( + "math" +) + +func _newContext(runtime *_runtime) { +@{[ join "\n", $self->newContext() ]} +} + +func newConsoleObject(runtime *_runtime) *_object { +@{[ join "\n", $self->newConsoleObject() ]} +} +_END_ + +for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) { + $fmt->print(<<_END_); + +func toValue_$_(value $_) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} +_END_ +} + +$fmt->print(<<_END_); + +func toValue_string(value string) Value { + return Value{ + _valueType: valueString, + value: value, + } +} + +func toValue_string16(value []uint16) Value { + return Value{ + _valueType: valueString, + value: value, + } +} + +func toValue_bool(value bool) Value { + return Value{ + _valueType: valueBoolean, + value: value, + } +} + +func toValue_object(value *_object) Value { + return Value{ + _valueType: valueObject, + value: value, + } +} +_END_ + +close $fmt; + +sub newConsoleObject { + my $self = shift; + + return + $self->block(sub { + my $class = "Console"; + my @got = $self->functionDeclare( + $class, + "log", 0, + "debug:log", 0, + "info:log", 0, + "error", 0, + "warn:error", 0, + "dir", 0, + "time", 0, + "timeEnd", 0, + "trace", 0, + "assert", 0, + ); + return + "return @{[ $self->newObject(@got) ]}" + }), + ; +} + +sub newContext { + my $self = shift; + return + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + undef, + "prototypeValueObject", + ), + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueFunction", + ), + }), + + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + my @got = $self->functionDeclare( + $class, + "valueOf", 0, + "toString", 0, + "toLocaleString", 0, + "hasOwnProperty", 1, + "isPrototypeOf", 1, + "propertyIsEnumerable", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "apply", 2, + "call", 1, + "bind", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + $self->property("length", $self->numberValue(0), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # Object + $self->block(sub { + my $class = "Object"; + return + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "getPrototypeOf", 1, + "getOwnPropertyDescriptor", 2, + "defineProperty", 3, + "defineProperties", 2, + "create", 2, + "isExtensible", 1, + "preventExtensions", 1, + "isSealed", 1, + "seal", 1, + "isFrozen", 1, + "freeze", 1, + "keys", 1, + "getOwnPropertyNames", 1, + ), + ), + }), + + # Function + $self->block(sub { + my $class = "Function"; + return + "Function :=", + $self->globalFunction( + $class, + 1, + ), + ".$class = Function", + }), + + # Array + $self->block(sub { + my $class = "Array"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toLocaleString", 0, + "concat", 1, + "join", 1, + "splice", 2, + "shift", 0, + "pop", 0, + "push", 1, + "slice", 2, + "unshift", 1, + "reverse", 0, + "sort", 1, + "indexOf", 1, + "lastIndexOf", 1, + "every", 1, + "some", 1, + "forEach", 1, + "map", 1, + "filter", 1, + "reduce", 1, + "reduceRight", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classArray", + ".ObjectPrototype", + undef, + $self->property("length", $self->numberValue("uint32(0)"), "0100"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "isArray", 1, + ), + ), + }), + + # String + $self->block(sub { + my $class = "String"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "charAt", 1, + "charCodeAt", 1, + "concat", 1, + "indexOf", 1, + "lastIndexOf", 1, + "match", 1, + "replace", 2, + "search", 1, + "split", 2, + "slice", 2, + "substring", 2, + "toLowerCase", 0, + "toUpperCase", 0, + "substr", 2, + "trim", 0, + "trimLeft", 0, + "trimRight", 0, + "localeCompare", 1, + "toLocaleLowerCase", 0, + "toLocaleUpperCase", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classString", + ".ObjectPrototype", + "prototypeValueString", + $self->property("length", $self->numberValue("int(0)"), "0"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "fromCharCode", 1, + ), + ), + }), + + # Boolean + $self->block(sub { + my $class = "Boolean"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueBoolean", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + # Number + $self->block(sub { + my $class = "Number"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "toFixed", 1, + "toExponential", 1, + "toPrecision", 1, + "toLocaleString", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueNumber", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + $self->numberConstantDeclare( + "MAX_VALUE", "math.MaxFloat64", + "MIN_VALUE", "math.SmallestNonzeroFloat64", + "NaN", "math.NaN()", + "NEGATIVE_INFINITY", "math.Inf(-1)", + "POSITIVE_INFINITY", "math.Inf(+1)", + ), + ), + }), + + # Math + $self->block(sub { + my $class = "Math"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "abs", 1, + "acos", 1, + "asin", 1, + "atan", 1, + "atan2", 1, + "ceil", 1, + "cos", 1, + "exp", 1, + "floor", 1, + "log", 1, + "max", 2, + "min", 2, + "pow", 2, + "random", 0, + "round", 1, + "sin", 1, + "sqrt", 1, + "tan", 1, + ), + $self->numberConstantDeclare( + "E", "math.E", + "LN10", "math.Ln10", + "LN2", "math.Ln2", + "LOG2E", "math.Log2E", + "LOG10E", "math.Log10E", + "PI", "math.Pi", + "SQRT1_2", "sqrt1_2", + "SQRT2", "math.Sqrt2", + ) + ), + }), + + # Date + $self->block(sub { + my $class = "Date"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toDateString", 0, + "toTimeString", 0, + "toUTCString", 0, + "toISOString", 0, + "toJSON", 1, + "toGMTString", 0, + "toLocaleString", 0, + "toLocaleDateString", 0, + "toLocaleTimeString", 0, + "valueOf", 0, + "getTime", 0, + "getYear", 0, + "getFullYear", 0, + "getUTCFullYear", 0, + "getMonth", 0, + "getUTCMonth", 0, + "getDate", 0, + "getUTCDate", 0, + "getDay", 0, + "getUTCDay", 0, + "getHours", 0, + "getUTCHours", 0, + "getMinutes", 0, + "getUTCMinutes", 0, + "getSeconds", 0, + "getUTCSeconds", 0, + "getMilliseconds", 0, + "getUTCMilliseconds", 0, + "getTimezoneOffset", 0, + "setTime", 1, + "setMilliseconds", 1, + "setUTCMilliseconds", 1, + "setSeconds", 2, + "setUTCSeconds", 2, + "setMinutes", 3, + "setUTCMinutes", 3, + "setHours", 4, + "setUTCHours", 4, + "setDate", 1, + "setUTCDate", 1, + "setMonth", 2, + "setUTCMonth", 2, + "setYear", 1, + "setFullYear", 3, + "setUTCFullYear", 3, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueDate", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 7, + $self->functionDeclare( + $class, + "parse", 1, + "UTC", 7, + "now", 0, + ), + ), + }), + + # RegExp + $self->block(sub { + my $class = "RegExp"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "exec", 1, + "test", 1, + "compile", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueRegExp", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 2, + $self->functionDeclare( + $class, + ), + ), + }), + + # Error + $self->block(sub { + my $class = "Error"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + undef, + @got, + $self->property("name", $self->stringValue("Error")), + $self->property("message", $self->stringValue("")), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + (map { + my $class = "${_}Error"; + $self->block(sub { + my @got = $self->functionDeclare( + $class, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ErrorPrototype", + undef, + @got, + $self->property("name", $self->stringValue($class)), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }); + } qw/Eval Type Range Reference Syntax URI/), + + # JSON + $self->block(sub { + my $class = "JSON"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "parse", 2, + "stringify", 3, + ), + ), + }), + + # Global + $self->block(sub { + my $class = "Global"; + my @got = $self->functionDeclare( + $class, + "eval", 1, + "parseInt", 2, + "parseFloat", 1, + "isNaN", 1, + "isFinite", 1, + "decodeURI", 1, + "decodeURIComponent", 1, + "encodeURI", 1, + "encodeURIComponent", 1, + "escape", 1, + "unescape", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->globalDeclare( + "Object", + "Function", + "Array", + "String", + "Boolean", + "Number", + "Math", + "Date", + "RegExp", + "Error", + "EvalError", + "TypeError", + "RangeError", + "ReferenceError", + "SyntaxError", + "URIError", + "JSON", + ), + $self->property("undefined", $self->undefinedValue(), "0"), + $self->property("NaN", $self->numberValue("math.NaN()"), "0"), + $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + "runtime.GlobalObject.property =", + @propertyMap, + "runtime.GlobalObject.propertyOrder =", + $propertyOrder, + ; + }), + ; +} + +sub propertyMap { + my $self = shift; + return "map[string]_property{", (join ",\n", @_, ""), "}", +} + +our (@preblock, @postblock); +sub block { + my $self = shift; + local @preblock = (); + local @postblock = (); + my @input = $_[0]->(); + my @output; + while (@input) { + local $_ = shift @input; + if (m/^\./) { + $_ = "runtime.Global$_"; + } + if (m/ :?=$/) { + $_ .= shift @input; + } + push @output, $_; + } + return + "{", + @preblock, + @output, + @postblock, + "}", + ; +} + +sub numberConstantDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + my $value = shift; + push @got, $self->property($name, $self->numberValue($value), "0"), + } + return @got; +} + +sub functionDeclare { + my $self = shift; + my $class = shift; + my $builtin = "builtin${class}"; + my @got; + while (@_) { + my $name = shift; + my $length = shift; + $name = $self->newFunction($name, "${builtin}_", $length); + push @got, $self->functionProperty($name), + } + return @got; +} + +sub globalDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + push @got, $self->property($name, $self->objectValue("runtime.Global.$name"), "0101"), + } + return @got; +} + +sub propertyOrder { + my $self = shift; + my $propertyMap = join "", @_; + + my (@keys) = $propertyMap =~ m/("\w+"):/g; + my $propertyOrder = + join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}"; + return $propertyOrder; +} + +sub globalObject { + my $self = shift; + my $name = shift; + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$name", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + $propertyMap +} +_END_ +} + +sub globalFunction { + my $self = shift; + my $name = shift; + my $length = shift; + + my $builtin = "builtin${name}"; + my $builtinNew = "builtinNew${name}"; + my $prototype = "runtime.Global.${name}Prototype"; + my $propertyMap = ""; + unshift @_, + $self->property("length", $self->numberValue($length), "0"), + $self->property("prototype", $self->objectValue($prototype), "0"), + ; + + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + push @postblock, $self->statement( + "$prototype.property[\"constructor\"] =", + $self->property(undef, $self->objectValue("runtime.Global.${name}"), "0101"), + ); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: @{[ $self->functionOf($self->nativeCallFunction($name, $builtin), $builtinNew) ]}, + $propertyMap +} +_END_ +} + +sub nativeCallFunction { + my $self = shift; + my $name = shift; + my $func = shift; + return trim <<_END_; +_nativeCallFunction{ "$name", $func } +_END_ +} + +sub globalPrototype { + my $self = shift; + my $class = shift; + my $classObject = shift; + my $prototype = shift; + my $value = shift; + + if (!defined $prototype) { + $prototype = "nil"; + } + + if (!defined $value) { + $value = "nil"; + } + + if ($prototype =~ m/^\./) { + $prototype = "runtime.Global$prototype"; + } + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $classObject, + prototype: $prototype, + extensible: true, + value: $value, + $propertyMap +} +_END_ +} + +sub newFunction { + my $self = shift; + my $name = shift; + my $func = shift; + my $length = shift; + + my @name = ($name, $name); + if ($name =~ m/^(\w+):(\w+)$/) { + @name = ($1, $2); + $name = $name[0]; + } + + if ($func =~ m/^builtin\w+_$/) { + $func = "$func$name[1]"; + } + + my $propertyOrder = ""; + my @propertyMap = ( + $self->property("length", $self->numberValue($length), "0"), + ); + + if (@propertyMap) { + $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder = "$propertyOrder,"; + } + + my $label = functionLabel($name); + push @preblock, $self->statement( + "$label := @{[ trim <<_END_ ]}", +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, + $propertyOrder + value: @{[ $self->functionOf($self->nativeCallFunction($name, $func)) ]}, +} +_END_ + ); + + return $name; +} + +sub newObject { + my $self = shift; + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, +} +_END_ +} + +sub newPrototypeObject { + my $self = shift; + my $class = shift; + my $objectClass = shift; + my $value = shift; + if (defined $value) { + $value = "value: $value,"; + } + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $objectClass, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, + $value +} +_END_ +} + +sub functionProperty { + my $self = shift; + my $name = shift; + + return $self->property( + $name, + $self->objectValue(functionLabel($name)) + ); +} + +sub statement { + my $self = shift; + return join "\n", @_; +} + +sub functionOf { + my $self = shift; + my $call = shift; + my $construct = shift; + if ($construct) { + $construct = "construct: $construct,"; + } else { + $construct = ""; + } + + return trim <<_END_ +_functionObject{ + call: $call, + $construct +} +_END_ +} + +sub nameProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: $value, +} +_END_ +} + +sub numberValue { + my $self = shift; + my $value = shift; + return trim <<_END_; +Value{ + _valueType: valueNumber, + value: $value, +} +_END_ +} + +sub property { + my $self = shift; + my $name = shift; + my $value = shift; + my $mode = shift; + $mode = "0101" unless defined $mode; + if (! defined $value) { + $value = "Value{}"; + } + if (defined $name) { + return trim <<_END_; +"$name": _property{ + mode: $mode, + value: $value, +} +_END_ + } else { + return trim <<_END_; +_property{ + mode: $mode, + value: $value, +} +_END_ + } + +} + +sub objectProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: @{[ $self->objectValue($value)]}, +} +_END_ +} + +sub objectValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + _valueType: valueObject, + value: $value, +} +_END_ +} + +sub stringValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + _valueType: valueString, + value: "$value", +} +_END_ +} + +sub booleanValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + _valueType: valueBoolean, + value: $value, +} +_END_ +} + +sub undefinedValue { + my $self = shift; + return trim <<_END_ +Value{ + _valueType: valueUndefined, +} +_END_ +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/inline.go b/Godeps/_workspace/src/github.com/obscuren/otto/inline.go new file mode 100644 index 000000000..f8c1d6347 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/inline.go @@ -0,0 +1,6463 @@ +package otto + +import ( + "math" +) + +func _newContext(runtime *_runtime) { + { + runtime.Global.ObjectPrototype = &_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: nil, + extensible: true, + value: prototypeValueObject, + } + } + { + runtime.Global.FunctionPrototype = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueFunction, + } + } + { + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"valueOf", builtinObject_valueOf}, + }, + } + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinObject_toString}, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleString", builtinObject_toLocaleString}, + }, + } + hasOwnProperty_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"hasOwnProperty", builtinObject_hasOwnProperty}, + }, + } + isPrototypeOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isPrototypeOf", builtinObject_isPrototypeOf}, + }, + } + propertyIsEnumerable_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"propertyIsEnumerable", builtinObject_propertyIsEnumerable}, + }, + } + runtime.Global.ObjectPrototype.property = map[string]_property{ + "valueOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: valueOf_function, + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleString_function, + }, + }, + "hasOwnProperty": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: hasOwnProperty_function, + }, + }, + "isPrototypeOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isPrototypeOf_function, + }, + }, + "propertyIsEnumerable": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: propertyIsEnumerable_function, + }, + }, + "constructor": _property{ + mode: 0101, + value: Value{}, + }, + } + runtime.Global.ObjectPrototype.propertyOrder = []string{ + "valueOf", + "toString", + "toLocaleString", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor", + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinFunction_toString}, + }, + } + apply_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"apply", builtinFunction_apply}, + }, + } + call_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"call", builtinFunction_call}, + }, + } + bind_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"bind", builtinFunction_bind}, + }, + } + runtime.Global.FunctionPrototype.property = map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "apply": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: apply_function, + }, + }, + "call": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: call_function, + }, + }, + "bind": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: bind_function, + }, + }, + "constructor": _property{ + mode: 0101, + value: Value{}, + }, + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + } + runtime.Global.FunctionPrototype.propertyOrder = []string{ + "toString", + "apply", + "call", + "bind", + "constructor", + "length", + } + } + { + getPrototypeOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getPrototypeOf", builtinObject_getPrototypeOf}, + }, + } + getOwnPropertyDescriptor_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getOwnPropertyDescriptor", builtinObject_getOwnPropertyDescriptor}, + }, + } + defineProperty_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"defineProperty", builtinObject_defineProperty}, + }, + } + defineProperties_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"defineProperties", builtinObject_defineProperties}, + }, + } + create_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"create", builtinObject_create}, + }, + } + isExtensible_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isExtensible", builtinObject_isExtensible}, + }, + } + preventExtensions_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"preventExtensions", builtinObject_preventExtensions}, + }, + } + isSealed_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isSealed", builtinObject_isSealed}, + }, + } + seal_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"seal", builtinObject_seal}, + }, + } + isFrozen_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isFrozen", builtinObject_isFrozen}, + }, + } + freeze_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"freeze", builtinObject_freeze}, + }, + } + keys_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"keys", builtinObject_keys}, + }, + } + getOwnPropertyNames_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getOwnPropertyNames", builtinObject_getOwnPropertyNames}, + }, + } + runtime.Global.Object = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Object", builtinObject}, + construct: builtinNewObject, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ObjectPrototype, + }, + }, + "getPrototypeOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getPrototypeOf_function, + }, + }, + "getOwnPropertyDescriptor": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getOwnPropertyDescriptor_function, + }, + }, + "defineProperty": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: defineProperty_function, + }, + }, + "defineProperties": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: defineProperties_function, + }, + }, + "create": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: create_function, + }, + }, + "isExtensible": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isExtensible_function, + }, + }, + "preventExtensions": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: preventExtensions_function, + }, + }, + "isSealed": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isSealed_function, + }, + }, + "seal": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: seal_function, + }, + }, + "isFrozen": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isFrozen_function, + }, + }, + "freeze": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: freeze_function, + }, + }, + "keys": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: keys_function, + }, + }, + "getOwnPropertyNames": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getOwnPropertyNames_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "getPrototypeOf", + "getOwnPropertyDescriptor", + "defineProperty", + "defineProperties", + "create", + "isExtensible", + "preventExtensions", + "isSealed", + "seal", + "isFrozen", + "freeze", + "keys", + "getOwnPropertyNames", + }, + } + runtime.Global.ObjectPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Object, + }, + } + } + { + Function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Function", builtinFunction}, + construct: builtinNewFunction, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.FunctionPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.Function = Function + runtime.Global.FunctionPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Function, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinArray_toString}, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleString", builtinArray_toLocaleString}, + }, + } + concat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"concat", builtinArray_concat}, + }, + } + join_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"join", builtinArray_join}, + }, + } + splice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"splice", builtinArray_splice}, + }, + } + shift_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"shift", builtinArray_shift}, + }, + } + pop_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"pop", builtinArray_pop}, + }, + } + push_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"push", builtinArray_push}, + }, + } + slice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"slice", builtinArray_slice}, + }, + } + unshift_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"unshift", builtinArray_unshift}, + }, + } + reverse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"reverse", builtinArray_reverse}, + }, + } + sort_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"sort", builtinArray_sort}, + }, + } + indexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"indexOf", builtinArray_indexOf}, + }, + } + lastIndexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"lastIndexOf", builtinArray_lastIndexOf}, + }, + } + every_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"every", builtinArray_every}, + }, + } + some_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"some", builtinArray_some}, + }, + } + forEach_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"forEach", builtinArray_forEach}, + }, + } + map_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"map", builtinArray_map}, + }, + } + filter_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"filter", builtinArray_filter}, + }, + } + reduce_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"reduce", builtinArray_reduce}, + }, + } + reduceRight_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"reduceRight", builtinArray_reduceRight}, + }, + } + isArray_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isArray", builtinArray_isArray}, + }, + } + runtime.Global.ArrayPrototype = &_object{ + runtime: runtime, + class: "Array", + objectClass: _classArray, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "length": _property{ + mode: 0100, + value: Value{ + _valueType: valueNumber, + value: uint32(0), + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleString_function, + }, + }, + "concat": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: concat_function, + }, + }, + "join": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: join_function, + }, + }, + "splice": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: splice_function, + }, + }, + "shift": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: shift_function, + }, + }, + "pop": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: pop_function, + }, + }, + "push": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: push_function, + }, + }, + "slice": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: slice_function, + }, + }, + "unshift": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: unshift_function, + }, + }, + "reverse": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: reverse_function, + }, + }, + "sort": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: sort_function, + }, + }, + "indexOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: indexOf_function, + }, + }, + "lastIndexOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: lastIndexOf_function, + }, + }, + "every": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: every_function, + }, + }, + "some": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: some_function, + }, + }, + "forEach": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: forEach_function, + }, + }, + "map": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: map_function, + }, + }, + "filter": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: filter_function, + }, + }, + "reduce": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: reduce_function, + }, + }, + "reduceRight": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: reduceRight_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "toString", + "toLocaleString", + "concat", + "join", + "splice", + "shift", + "pop", + "push", + "slice", + "unshift", + "reverse", + "sort", + "indexOf", + "lastIndexOf", + "every", + "some", + "forEach", + "map", + "filter", + "reduce", + "reduceRight", + }, + } + runtime.Global.Array = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Array", builtinArray}, + construct: builtinNewArray, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ArrayPrototype, + }, + }, + "isArray": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isArray_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "isArray", + }, + } + runtime.Global.ArrayPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Array, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinString_toString}, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"valueOf", builtinString_valueOf}, + }, + } + charAt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"charAt", builtinString_charAt}, + }, + } + charCodeAt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"charCodeAt", builtinString_charCodeAt}, + }, + } + concat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"concat", builtinString_concat}, + }, + } + indexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"indexOf", builtinString_indexOf}, + }, + } + lastIndexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"lastIndexOf", builtinString_lastIndexOf}, + }, + } + match_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"match", builtinString_match}, + }, + } + replace_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"replace", builtinString_replace}, + }, + } + search_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"search", builtinString_search}, + }, + } + split_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"split", builtinString_split}, + }, + } + slice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"slice", builtinString_slice}, + }, + } + substring_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"substring", builtinString_substring}, + }, + } + toLowerCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLowerCase", builtinString_toLowerCase}, + }, + } + toUpperCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toUpperCase", builtinString_toUpperCase}, + }, + } + substr_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"substr", builtinString_substr}, + }, + } + trim_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"trim", builtinString_trim}, + }, + } + trimLeft_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"trimLeft", builtinString_trimLeft}, + }, + } + trimRight_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"trimRight", builtinString_trimRight}, + }, + } + localeCompare_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"localeCompare", builtinString_localeCompare}, + }, + } + toLocaleLowerCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleLowerCase", builtinString_toLocaleLowerCase}, + }, + } + toLocaleUpperCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleUpperCase", builtinString_toLocaleUpperCase}, + }, + } + fromCharCode_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"fromCharCode", builtinString_fromCharCode}, + }, + } + runtime.Global.StringPrototype = &_object{ + runtime: runtime, + class: "String", + objectClass: _classString, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueString, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: int(0), + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: valueOf_function, + }, + }, + "charAt": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: charAt_function, + }, + }, + "charCodeAt": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: charCodeAt_function, + }, + }, + "concat": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: concat_function, + }, + }, + "indexOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: indexOf_function, + }, + }, + "lastIndexOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: lastIndexOf_function, + }, + }, + "match": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: match_function, + }, + }, + "replace": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: replace_function, + }, + }, + "search": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: search_function, + }, + }, + "split": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: split_function, + }, + }, + "slice": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: slice_function, + }, + }, + "substring": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: substring_function, + }, + }, + "toLowerCase": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLowerCase_function, + }, + }, + "toUpperCase": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toUpperCase_function, + }, + }, + "substr": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: substr_function, + }, + }, + "trim": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: trim_function, + }, + }, + "trimLeft": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: trimLeft_function, + }, + }, + "trimRight": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: trimRight_function, + }, + }, + "localeCompare": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: localeCompare_function, + }, + }, + "toLocaleLowerCase": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleLowerCase_function, + }, + }, + "toLocaleUpperCase": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleUpperCase_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "toString", + "valueOf", + "charAt", + "charCodeAt", + "concat", + "indexOf", + "lastIndexOf", + "match", + "replace", + "search", + "split", + "slice", + "substring", + "toLowerCase", + "toUpperCase", + "substr", + "trim", + "trimLeft", + "trimRight", + "localeCompare", + "toLocaleLowerCase", + "toLocaleUpperCase", + }, + } + runtime.Global.String = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"String", builtinString}, + construct: builtinNewString, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.StringPrototype, + }, + }, + "fromCharCode": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: fromCharCode_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "fromCharCode", + }, + } + runtime.Global.StringPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.String, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinBoolean_toString}, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"valueOf", builtinBoolean_valueOf}, + }, + } + runtime.Global.BooleanPrototype = &_object{ + runtime: runtime, + class: "Boolean", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueBoolean, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: valueOf_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "valueOf", + }, + } + runtime.Global.Boolean = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Boolean", builtinBoolean}, + construct: builtinNewBoolean, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.BooleanPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.BooleanPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Boolean, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinNumber_toString}, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"valueOf", builtinNumber_valueOf}, + }, + } + toFixed_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toFixed", builtinNumber_toFixed}, + }, + } + toExponential_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toExponential", builtinNumber_toExponential}, + }, + } + toPrecision_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toPrecision", builtinNumber_toPrecision}, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleString", builtinNumber_toLocaleString}, + }, + } + runtime.Global.NumberPrototype = &_object{ + runtime: runtime, + class: "Number", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueNumber, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: valueOf_function, + }, + }, + "toFixed": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toFixed_function, + }, + }, + "toExponential": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toExponential_function, + }, + }, + "toPrecision": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toPrecision_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleString_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "valueOf", + "toFixed", + "toExponential", + "toPrecision", + "toLocaleString", + }, + } + runtime.Global.Number = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Number", builtinNumber}, + construct: builtinNewNumber, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.NumberPrototype, + }, + }, + "MAX_VALUE": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.MaxFloat64, + }, + }, + "MIN_VALUE": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.SmallestNonzeroFloat64, + }, + }, + "NaN": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.NaN(), + }, + }, + "NEGATIVE_INFINITY": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Inf(-1), + }, + }, + "POSITIVE_INFINITY": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Inf(+1), + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "MAX_VALUE", + "MIN_VALUE", + "NaN", + "NEGATIVE_INFINITY", + "POSITIVE_INFINITY", + }, + } + runtime.Global.NumberPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Number, + }, + } + } + { + abs_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"abs", builtinMath_abs}, + }, + } + acos_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"acos", builtinMath_acos}, + }, + } + asin_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"asin", builtinMath_asin}, + }, + } + atan_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"atan", builtinMath_atan}, + }, + } + atan2_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"atan2", builtinMath_atan2}, + }, + } + ceil_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"ceil", builtinMath_ceil}, + }, + } + cos_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"cos", builtinMath_cos}, + }, + } + exp_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"exp", builtinMath_exp}, + }, + } + floor_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"floor", builtinMath_floor}, + }, + } + log_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"log", builtinMath_log}, + }, + } + max_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"max", builtinMath_max}, + }, + } + min_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"min", builtinMath_min}, + }, + } + pow_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"pow", builtinMath_pow}, + }, + } + random_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"random", builtinMath_random}, + }, + } + round_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"round", builtinMath_round}, + }, + } + sin_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"sin", builtinMath_sin}, + }, + } + sqrt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"sqrt", builtinMath_sqrt}, + }, + } + tan_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"tan", builtinMath_tan}, + }, + } + runtime.Global.Math = &_object{ + runtime: runtime, + class: "Math", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "abs": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: abs_function, + }, + }, + "acos": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: acos_function, + }, + }, + "asin": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: asin_function, + }, + }, + "atan": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: atan_function, + }, + }, + "atan2": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: atan2_function, + }, + }, + "ceil": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: ceil_function, + }, + }, + "cos": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: cos_function, + }, + }, + "exp": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: exp_function, + }, + }, + "floor": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: floor_function, + }, + }, + "log": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: log_function, + }, + }, + "max": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: max_function, + }, + }, + "min": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: min_function, + }, + }, + "pow": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: pow_function, + }, + }, + "random": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: random_function, + }, + }, + "round": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: round_function, + }, + }, + "sin": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: sin_function, + }, + }, + "sqrt": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: sqrt_function, + }, + }, + "tan": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: tan_function, + }, + }, + "E": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.E, + }, + }, + "LN10": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Ln10, + }, + }, + "LN2": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Ln2, + }, + }, + "LOG2E": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Log2E, + }, + }, + "LOG10E": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Log10E, + }, + }, + "PI": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Pi, + }, + }, + "SQRT1_2": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: sqrt1_2, + }, + }, + "SQRT2": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Sqrt2, + }, + }, + }, + propertyOrder: []string{ + "abs", + "acos", + "asin", + "atan", + "atan2", + "ceil", + "cos", + "exp", + "floor", + "log", + "max", + "min", + "pow", + "random", + "round", + "sin", + "sqrt", + "tan", + "E", + "LN10", + "LN2", + "LOG2E", + "LOG10E", + "PI", + "SQRT1_2", + "SQRT2", + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinDate_toString}, + }, + } + toDateString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toDateString", builtinDate_toDateString}, + }, + } + toTimeString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toTimeString", builtinDate_toTimeString}, + }, + } + toUTCString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toUTCString", builtinDate_toUTCString}, + }, + } + toISOString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toISOString", builtinDate_toISOString}, + }, + } + toJSON_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toJSON", builtinDate_toJSON}, + }, + } + toGMTString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toGMTString", builtinDate_toGMTString}, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleString", builtinDate_toLocaleString}, + }, + } + toLocaleDateString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleDateString", builtinDate_toLocaleDateString}, + }, + } + toLocaleTimeString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toLocaleTimeString", builtinDate_toLocaleTimeString}, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"valueOf", builtinDate_valueOf}, + }, + } + getTime_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getTime", builtinDate_getTime}, + }, + } + getYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getYear", builtinDate_getYear}, + }, + } + getFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getFullYear", builtinDate_getFullYear}, + }, + } + getUTCFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCFullYear", builtinDate_getUTCFullYear}, + }, + } + getMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getMonth", builtinDate_getMonth}, + }, + } + getUTCMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCMonth", builtinDate_getUTCMonth}, + }, + } + getDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getDate", builtinDate_getDate}, + }, + } + getUTCDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCDate", builtinDate_getUTCDate}, + }, + } + getDay_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getDay", builtinDate_getDay}, + }, + } + getUTCDay_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCDay", builtinDate_getUTCDay}, + }, + } + getHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getHours", builtinDate_getHours}, + }, + } + getUTCHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCHours", builtinDate_getUTCHours}, + }, + } + getMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getMinutes", builtinDate_getMinutes}, + }, + } + getUTCMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCMinutes", builtinDate_getUTCMinutes}, + }, + } + getSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getSeconds", builtinDate_getSeconds}, + }, + } + getUTCSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCSeconds", builtinDate_getUTCSeconds}, + }, + } + getMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getMilliseconds", builtinDate_getMilliseconds}, + }, + } + getUTCMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getUTCMilliseconds", builtinDate_getUTCMilliseconds}, + }, + } + getTimezoneOffset_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"getTimezoneOffset", builtinDate_getTimezoneOffset}, + }, + } + setTime_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setTime", builtinDate_setTime}, + }, + } + setMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setMilliseconds", builtinDate_setMilliseconds}, + }, + } + setUTCMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCMilliseconds", builtinDate_setUTCMilliseconds}, + }, + } + setSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setSeconds", builtinDate_setSeconds}, + }, + } + setUTCSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCSeconds", builtinDate_setUTCSeconds}, + }, + } + setMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setMinutes", builtinDate_setMinutes}, + }, + } + setUTCMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCMinutes", builtinDate_setUTCMinutes}, + }, + } + setHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 4, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setHours", builtinDate_setHours}, + }, + } + setUTCHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 4, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCHours", builtinDate_setUTCHours}, + }, + } + setDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setDate", builtinDate_setDate}, + }, + } + setUTCDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCDate", builtinDate_setUTCDate}, + }, + } + setMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setMonth", builtinDate_setMonth}, + }, + } + setUTCMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCMonth", builtinDate_setUTCMonth}, + }, + } + setYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setYear", builtinDate_setYear}, + }, + } + setFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setFullYear", builtinDate_setFullYear}, + }, + } + setUTCFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"setUTCFullYear", builtinDate_setUTCFullYear}, + }, + } + parse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"parse", builtinDate_parse}, + }, + } + UTC_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 7, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"UTC", builtinDate_UTC}, + }, + } + now_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"now", builtinDate_now}, + }, + } + runtime.Global.DatePrototype = &_object{ + runtime: runtime, + class: "Date", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueDate, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "toDateString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toDateString_function, + }, + }, + "toTimeString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toTimeString_function, + }, + }, + "toUTCString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toUTCString_function, + }, + }, + "toISOString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toISOString_function, + }, + }, + "toJSON": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toJSON_function, + }, + }, + "toGMTString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toGMTString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleString_function, + }, + }, + "toLocaleDateString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleDateString_function, + }, + }, + "toLocaleTimeString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toLocaleTimeString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: valueOf_function, + }, + }, + "getTime": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getTime_function, + }, + }, + "getYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getYear_function, + }, + }, + "getFullYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getFullYear_function, + }, + }, + "getUTCFullYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCFullYear_function, + }, + }, + "getMonth": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getMonth_function, + }, + }, + "getUTCMonth": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCMonth_function, + }, + }, + "getDate": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getDate_function, + }, + }, + "getUTCDate": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCDate_function, + }, + }, + "getDay": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getDay_function, + }, + }, + "getUTCDay": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCDay_function, + }, + }, + "getHours": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getHours_function, + }, + }, + "getUTCHours": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCHours_function, + }, + }, + "getMinutes": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getMinutes_function, + }, + }, + "getUTCMinutes": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCMinutes_function, + }, + }, + "getSeconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getSeconds_function, + }, + }, + "getUTCSeconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCSeconds_function, + }, + }, + "getMilliseconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getMilliseconds_function, + }, + }, + "getUTCMilliseconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getUTCMilliseconds_function, + }, + }, + "getTimezoneOffset": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: getTimezoneOffset_function, + }, + }, + "setTime": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setTime_function, + }, + }, + "setMilliseconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setMilliseconds_function, + }, + }, + "setUTCMilliseconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCMilliseconds_function, + }, + }, + "setSeconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setSeconds_function, + }, + }, + "setUTCSeconds": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCSeconds_function, + }, + }, + "setMinutes": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setMinutes_function, + }, + }, + "setUTCMinutes": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCMinutes_function, + }, + }, + "setHours": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setHours_function, + }, + }, + "setUTCHours": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCHours_function, + }, + }, + "setDate": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setDate_function, + }, + }, + "setUTCDate": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCDate_function, + }, + }, + "setMonth": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setMonth_function, + }, + }, + "setUTCMonth": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCMonth_function, + }, + }, + "setYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setYear_function, + }, + }, + "setFullYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setFullYear_function, + }, + }, + "setUTCFullYear": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: setUTCFullYear_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "toDateString", + "toTimeString", + "toUTCString", + "toISOString", + "toJSON", + "toGMTString", + "toLocaleString", + "toLocaleDateString", + "toLocaleTimeString", + "valueOf", + "getTime", + "getYear", + "getFullYear", + "getUTCFullYear", + "getMonth", + "getUTCMonth", + "getDate", + "getUTCDate", + "getDay", + "getUTCDay", + "getHours", + "getUTCHours", + "getMinutes", + "getUTCMinutes", + "getSeconds", + "getUTCSeconds", + "getMilliseconds", + "getUTCMilliseconds", + "getTimezoneOffset", + "setTime", + "setMilliseconds", + "setUTCMilliseconds", + "setSeconds", + "setUTCSeconds", + "setMinutes", + "setUTCMinutes", + "setHours", + "setUTCHours", + "setDate", + "setUTCDate", + "setMonth", + "setUTCMonth", + "setYear", + "setFullYear", + "setUTCFullYear", + }, + } + runtime.Global.Date = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Date", builtinDate}, + construct: builtinNewDate, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 7, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.DatePrototype, + }, + }, + "parse": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: parse_function, + }, + }, + "UTC": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: UTC_function, + }, + }, + "now": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: now_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "parse", + "UTC", + "now", + }, + } + runtime.Global.DatePrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Date, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinRegExp_toString}, + }, + } + exec_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"exec", builtinRegExp_exec}, + }, + } + test_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"test", builtinRegExp_test}, + }, + } + compile_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"compile", builtinRegExp_compile}, + }, + } + runtime.Global.RegExpPrototype = &_object{ + runtime: runtime, + class: "RegExp", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: prototypeValueRegExp, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "exec": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: exec_function, + }, + }, + "test": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: test_function, + }, + }, + "compile": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: compile_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "exec", + "test", + "compile", + }, + } + runtime.Global.RegExp = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"RegExp", builtinRegExp}, + construct: builtinNewRegExp, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RegExpPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.RegExpPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RegExp, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"toString", builtinError_toString}, + }, + } + runtime.Global.ErrorPrototype = &_object{ + runtime: runtime, + class: "Error", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: toString_function, + }, + }, + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "Error", + }, + }, + "message": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "", + }, + }, + }, + propertyOrder: []string{ + "toString", + "name", + "message", + }, + } + runtime.Global.Error = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"Error", builtinError}, + construct: builtinNewError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.ErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Error, + }, + } + } + { + runtime.Global.EvalErrorPrototype = &_object{ + runtime: runtime, + class: "EvalError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "EvalError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.EvalError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"EvalError", builtinEvalError}, + construct: builtinNewEvalError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.EvalErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.EvalErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.EvalError, + }, + } + } + { + runtime.Global.TypeErrorPrototype = &_object{ + runtime: runtime, + class: "TypeError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "TypeError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.TypeError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"TypeError", builtinTypeError}, + construct: builtinNewTypeError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.TypeErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.TypeErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.TypeError, + }, + } + } + { + runtime.Global.RangeErrorPrototype = &_object{ + runtime: runtime, + class: "RangeError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "RangeError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.RangeError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"RangeError", builtinRangeError}, + construct: builtinNewRangeError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RangeErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.RangeErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RangeError, + }, + } + } + { + runtime.Global.ReferenceErrorPrototype = &_object{ + runtime: runtime, + class: "ReferenceError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "ReferenceError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.ReferenceError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"ReferenceError", builtinReferenceError}, + construct: builtinNewReferenceError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ReferenceErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.ReferenceErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ReferenceError, + }, + } + } + { + runtime.Global.SyntaxErrorPrototype = &_object{ + runtime: runtime, + class: "SyntaxError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "SyntaxError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.SyntaxError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"SyntaxError", builtinSyntaxError}, + construct: builtinNewSyntaxError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.SyntaxErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.SyntaxErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.SyntaxError, + }, + } + } + { + runtime.Global.URIErrorPrototype = &_object{ + runtime: runtime, + class: "URIError", + objectClass: _classObject, + prototype: runtime.Global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + _valueType: valueString, + value: "URIError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.Global.URIError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + value: _functionObject{ + call: _nativeCallFunction{"URIError", builtinURIError}, + construct: builtinNewURIError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + _valueType: valueObject, + value: runtime.Global.URIErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.Global.URIErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.URIError, + }, + } + } + { + parse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"parse", builtinJSON_parse}, + }, + } + stringify_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"stringify", builtinJSON_stringify}, + }, + } + runtime.Global.JSON = &_object{ + runtime: runtime, + class: "JSON", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "parse": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: parse_function, + }, + }, + "stringify": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: stringify_function, + }, + }, + }, + propertyOrder: []string{ + "parse", + "stringify", + }, + } + } + { + eval_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"eval", builtinGlobal_eval}, + }, + } + parseInt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"parseInt", builtinGlobal_parseInt}, + }, + } + parseFloat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"parseFloat", builtinGlobal_parseFloat}, + }, + } + isNaN_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isNaN", builtinGlobal_isNaN}, + }, + } + isFinite_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"isFinite", builtinGlobal_isFinite}, + }, + } + decodeURI_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"decodeURI", builtinGlobal_decodeURI}, + }, + } + decodeURIComponent_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"decodeURIComponent", builtinGlobal_decodeURIComponent}, + }, + } + encodeURI_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"encodeURI", builtinGlobal_encodeURI}, + }, + } + encodeURIComponent_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"encodeURIComponent", builtinGlobal_encodeURIComponent}, + }, + } + escape_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"escape", builtinGlobal_escape}, + }, + } + unescape_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"unescape", builtinGlobal_unescape}, + }, + } + runtime.GlobalObject.property = map[string]_property{ + "eval": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: eval_function, + }, + }, + "parseInt": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: parseInt_function, + }, + }, + "parseFloat": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: parseFloat_function, + }, + }, + "isNaN": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isNaN_function, + }, + }, + "isFinite": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: isFinite_function, + }, + }, + "decodeURI": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: decodeURI_function, + }, + }, + "decodeURIComponent": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: decodeURIComponent_function, + }, + }, + "encodeURI": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: encodeURI_function, + }, + }, + "encodeURIComponent": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: encodeURIComponent_function, + }, + }, + "escape": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: escape_function, + }, + }, + "unescape": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: unescape_function, + }, + }, + "Object": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Object, + }, + }, + "Function": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Function, + }, + }, + "Array": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Array, + }, + }, + "String": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.String, + }, + }, + "Boolean": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Boolean, + }, + }, + "Number": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Number, + }, + }, + "Math": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Math, + }, + }, + "Date": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Date, + }, + }, + "RegExp": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RegExp, + }, + }, + "Error": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.Error, + }, + }, + "EvalError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.EvalError, + }, + }, + "TypeError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.TypeError, + }, + }, + "RangeError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.RangeError, + }, + }, + "ReferenceError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.ReferenceError, + }, + }, + "SyntaxError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.SyntaxError, + }, + }, + "URIError": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.URIError, + }, + }, + "JSON": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: runtime.Global.JSON, + }, + }, + "undefined": _property{ + mode: 0, + value: Value{ + _valueType: valueUndefined, + }, + }, + "NaN": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.NaN(), + }, + }, + "Infinity": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: math.Inf(+1), + }, + }, + } + runtime.GlobalObject.propertyOrder = []string{ + "eval", + "parseInt", + "parseFloat", + "isNaN", + "isFinite", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "escape", + "unescape", + "Object", + "Function", + "Array", + "String", + "Boolean", + "Number", + "Math", + "Date", + "RegExp", + "Error", + "EvalError", + "TypeError", + "RangeError", + "ReferenceError", + "SyntaxError", + "URIError", + "JSON", + "undefined", + "NaN", + "Infinity", + } + } +} + +func newConsoleObject(runtime *_runtime) *_object { + { + log_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"log", builtinConsole_log}, + }, + } + debug_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"debug", builtinConsole_log}, + }, + } + info_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"info", builtinConsole_log}, + }, + } + error_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"error", builtinConsole_error}, + }, + } + warn_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"warn", builtinConsole_error}, + }, + } + dir_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"dir", builtinConsole_dir}, + }, + } + time_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"time", builtinConsole_time}, + }, + } + timeEnd_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"timeEnd", builtinConsole_timeEnd}, + }, + } + trace_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"trace", builtinConsole_trace}, + }, + } + assert_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.Global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + _valueType: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _functionObject{ + call: _nativeCallFunction{"assert", builtinConsole_assert}, + }, + } + return &_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: runtime.Global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "log": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: log_function, + }, + }, + "debug": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: debug_function, + }, + }, + "info": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: info_function, + }, + }, + "error": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: error_function, + }, + }, + "warn": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: warn_function, + }, + }, + "dir": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: dir_function, + }, + }, + "time": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: time_function, + }, + }, + "timeEnd": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: timeEnd_function, + }, + }, + "trace": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: trace_function, + }, + }, + "assert": _property{ + mode: 0101, + value: Value{ + _valueType: valueObject, + value: assert_function, + }, + }, + }, + propertyOrder: []string{ + "log", + "debug", + "info", + "error", + "warn", + "dir", + "time", + "timeEnd", + "trace", + "assert", + }, + } + } +} + +func toValue_int(value int) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_int8(value int8) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_int16(value int16) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_int32(value int32) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_int64(value int64) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_uint(value uint) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_uint8(value uint8) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_uint16(value uint16) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_uint32(value uint32) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_uint64(value uint64) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_float32(value float32) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_float64(value float64) Value { + return Value{ + _valueType: valueNumber, + value: value, + } +} + +func toValue_string(value string) Value { + return Value{ + _valueType: valueString, + value: value, + } +} + +func toValue_string16(value []uint16) Value { + return Value{ + _valueType: valueString, + value: value, + } +} + +func toValue_bool(value bool) Value { + return Value{ + _valueType: valueBoolean, + value: value, + } +} + +func toValue_object(value *_object) Value { + return Value{ + _valueType: valueObject, + value: value, + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/json_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/json_test.go new file mode 100644 index 000000000..4dd2ed7bf --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/json_test.go @@ -0,0 +1,183 @@ +package otto + +import ( + "testing" +) + +func BenchmarkJSON_parse(b *testing.B) { + vm := New() + for i := 0; i < b.N; i++ { + vm.Run(`JSON.parse("1")`) + vm.Run(`JSON.parse("[1,2,3]")`) + vm.Run(`JSON.parse('{"a":{"x":100,"y":110},"b":[10,20,30],"c":"zazazaza"}')`) + vm.Run(`JSON.parse("[1,2,3]", function(k, v) { return undefined })`) + } +} + +func TestJSON_parse(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + JSON.parse("1"); + `, 1) + + test(` + JSON.parse("null"); + `, "null") // TODO Can we make this nil? + + test(` + var abc = JSON.parse('"a\uFFFFbc"'); + [ abc[0], abc[2], abc[3], abc.length ]; + `, "a,b,c,4") + + test(` + JSON.parse("[1, 2, 3]"); + `, "1,2,3") + + test(` + JSON.parse('{ "abc": 1, "def":2 }').abc; + `, 1) + + test(` + JSON.parse('{ "abc": { "x": 100, "y": 110 }, "def": [ 10, 20 ,30 ], "ghi": "zazazaza" }').def; + `, "10,20,30") + + test(`raise: + JSON.parse("12\t\r\n 34"); + `, "SyntaxError: invalid character '3' after top-level value") + + test(` + JSON.parse("[1, 2, 3]", function() { return undefined }); + `, "undefined") + + test(`raise: + JSON.parse(""); + `, "SyntaxError: unexpected end of JSON input") + + test(`raise: + JSON.parse("[1, 2, 3"); + `, "SyntaxError: unexpected end of JSON input") + + test(`raise: + JSON.parse("[1, 2, ; abc=10"); + `, "SyntaxError: invalid character ';' looking for beginning of value") + + test(`raise: + JSON.parse("[1, 2, function(){}]"); + `, "SyntaxError: invalid character 'u' in literal false (expecting 'a')") + }) +} + +func TestJSON_stringify(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + JSON.stringify(function(){}); + `, "undefined") + + test(` + JSON.stringify(new Boolean(false)); + `, "false") + + test(` + JSON.stringify({a1: {b1: [1,2,3,4], b2: {c1: 1, c2: 2}}, a2: 'a2'}, null, -5); + `, `{"a1":{"b1":[1,2,3,4],"b2":{"c1":1,"c2":2}},"a2":"a2"}`) + + test(` + JSON.stringify(undefined); + `, "undefined") + + test(` + JSON.stringify(1); + `, "1") + + test(` + JSON.stringify("abc def"); + `, "\"abc def\"") + + test(` + JSON.stringify(3.14159); + `, "3.14159") + + test(` + JSON.stringify([]); + `, "[]") + + test(` + JSON.stringify([1, 2, 3]); + `, "[1,2,3]") + + test(` + JSON.stringify([true, false, null]); + `, "[true,false,null]") + + test(` + JSON.stringify({ + abc: { x: 100, y: 110 }, + def: [ 10, 20, 30 ], + ghi: "zazazaza" + }); + `, `{"abc":{"x":100,"y":110},"def":[10,20,30],"ghi":"zazazaza"}`) + + test(` + JSON.stringify([ + 'e', + {pluribus: 'unum'} + ], null, '\t'); + `, "[\n\t\"e\",\n\t{\n\t\t\"pluribus\": \"unum\"\n\t}\n]") + + test(` + JSON.stringify(new Date(0)); + `, `"1970-01-01T00:00:00.000Z"`) + + test(` + JSON.stringify([ new Date(0) ], function(key, value){ + return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value + }); + `, `["Date(Thu, 01 Jan 1970 00:00:00 UTC)"]`) + + test(` + JSON.stringify({ + abc: 1, + def: 2, + ghi: 3 + }, ['abc','def']); + `, `{"abc":1,"def":2}`) + + test(`raise: + var abc = { + def: null + }; + abc.def = abc; + JSON.stringify(abc) + `, "TypeError: Converting circular structure to JSON") + + test(`raise: + var abc= [ null ]; + abc[0] = abc; + JSON.stringify(abc); + `, "TypeError: Converting circular structure to JSON") + + test(`raise: + var abc = { + def: {} + }; + abc.def.ghi = abc; + JSON.stringify(abc) + `, "TypeError: Converting circular structure to JSON") + + test(` + var ghi = { "pi": 3.14159 }; + var abc = { + def: {} + }; + abc.ghi = ghi; + abc.def.ghi = ghi; + JSON.stringify(abc); + `, `{"def":{"ghi":{"pi":3.14159}},"ghi":{"pi":3.14159}}`) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/math_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/math_test.go new file mode 100644 index 000000000..499998b14 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/math_test.go @@ -0,0 +1,303 @@ +package otto + +import ( + "math" + "testing" +) + +var _NaN = math.NaN() +var _Infinity = math.Inf(1) + +func TestMath_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.toString()`, "[object Math]") + }) +} + +func TestMath_abs(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.abs(NaN)`, _NaN) + test(`Math.abs(2)`, 2) + test(`Math.abs(-2)`, 2) + test(`Math.abs(-Infinity)`, _Infinity) + + test(`Math.acos(0.5)`, 1.0471975511965976) + + test(`Math.abs('-1')`, 1) + test(`Math.abs(-2)`, 2) + test(`Math.abs(null)`, 0) + test(`Math.abs("string")`, _NaN) + test(`Math.abs()`, _NaN) + }) +} + +func TestMath_acos(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.acos(NaN)`, _NaN) + test(`Math.acos(2)`, _NaN) + test(`Math.acos(-2)`, _NaN) + test(`1/Math.acos(1)`, _Infinity) + + test(`Math.acos(0.5)`, 1.0471975511965976) + }) +} + +func TestMath_asin(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.asin(NaN)`, _NaN) + test(`Math.asin(2)`, _NaN) + test(`Math.asin(-2)`, _NaN) + test(`1/Math.asin(0)`, _Infinity) + test(`1/Math.asin(-0)`, -_Infinity) + + test(`Math.asin(0.5)`, 0.5235987755982989) + }) +} + +func TestMath_atan(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.atan(NaN)`, _NaN) + test(`1/Math.atan(0)`, _Infinity) + test(`1/Math.atan(-0)`, -_Infinity) + test(`Math.atan(Infinity)`, 1.5707963267948966) + test(`Math.atan(-Infinity)`, -1.5707963267948966) + + // freebsd/386 1.03 => 0.4636476090008061 + // darwin 1.03 => 0.46364760900080604 + test(`Math.atan(0.5).toPrecision(10)`, "0.463647609") + }) +} + +func TestMath_atan2(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.atan2()`, _NaN) + test(`Math.atan2(NaN)`, _NaN) + test(`Math.atan2(0, NaN)`, _NaN) + + test(`Math.atan2(1, 0)`, 1.5707963267948966) + test(`Math.atan2(1, -0)`, 1.5707963267948966) + + test(`1/Math.atan2(0, 1)`, _Infinity) + test(`1/Math.atan2(0, 0)`, _Infinity) + test(`Math.atan2(0, -0)`, 3.141592653589793) + test(`Math.atan2(0, -1)`, 3.141592653589793) + + test(`1/Math.atan2(-0, 1)`, -_Infinity) + test(`1/Math.atan2(-0, 0)`, -_Infinity) + test(`Math.atan2(-0, -0)`, -3.141592653589793) + test(`Math.atan2(-0, -1)`, -3.141592653589793) + + test(`Math.atan2(-1, 0)`, -1.5707963267948966) + test(`Math.atan2(-1, -0)`, -1.5707963267948966) + + test(`1/Math.atan2(1, Infinity)`, _Infinity) + test(`Math.atan2(1, -Infinity)`, 3.141592653589793) + test(`1/Math.atan2(-1, Infinity)`, -_Infinity) + test(`Math.atan2(-1, -Infinity)`, -3.141592653589793) + + test(`Math.atan2(Infinity, 1)`, 1.5707963267948966) + test(`Math.atan2(-Infinity, 1)`, -1.5707963267948966) + + test(`Math.atan2(Infinity, Infinity)`, 0.7853981633974483) + test(`Math.atan2(Infinity, -Infinity)`, 2.356194490192345) + test(`Math.atan2(-Infinity, Infinity)`, -0.7853981633974483) + test(`Math.atan2(-Infinity, -Infinity)`, -2.356194490192345) + }) +} + +func TestMath_ceil(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.ceil(NaN)`, _NaN) + test(`Math.ceil(+0)`, 0) + test(`1/Math.ceil(-0)`, -_Infinity) + test(`Math.ceil(Infinity)`, _Infinity) + test(`Math.ceil(-Infinity)`, -_Infinity) + test(`1/Math.ceil(-0.5)`, -_Infinity) + + test(`Math.ceil(-11)`, -11) + test(`Math.ceil(-0.5)`, 0) + test(`Math.ceil(1.5)`, 2) + }) +} + +func TestMath_cos(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.cos(NaN)`, _NaN) + test(`Math.cos(+0)`, 1) + test(`Math.cos(-0)`, 1) + test(`Math.cos(Infinity)`, _NaN) + test(`Math.cos(-Infinity)`, _NaN) + + test(`Math.cos(0.5)`, 0.8775825618903728) + }) +} + +func TestMath_exp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.exp(NaN)`, _NaN) + test(`Math.exp(+0)`, 1) + test(`Math.exp(-0)`, 1) + test(`Math.exp(Infinity)`, _Infinity) + test(`Math.exp(-Infinity)`, 0) + }) +} + +func TestMath_floor(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.floor(NaN)`, _NaN) + test(`Math.floor(+0)`, 0) + test(`1/Math.floor(-0)`, -_Infinity) + test(`Math.floor(Infinity)`, _Infinity) + test(`Math.floor(-Infinity)`, -_Infinity) + + test(`Math.floor(-11)`, -11) + test(`Math.floor(-0.5)`, -1) + test(`Math.floor(1.5)`, 1) + }) +} + +func TestMath_log(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.log(NaN)`, _NaN) + test(`Math.log(-1)`, _NaN) + test(`Math.log(+0)`, -_Infinity) + test(`Math.log(-0)`, -_Infinity) + test(`1/Math.log(1)`, _Infinity) + test(`Math.log(Infinity)`, _Infinity) + + test(`Math.log(0.5)`, -0.6931471805599453) + }) +} + +func TestMath_max(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.max(-11, -1, 0, 1, 2, 3, 11)`, 11) + }) +} + +func TestMath_min(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.min(-11, -1, 0, 1, 2, 3, 11)`, -11) + }) +} + +func TestMath_pow(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.pow(0, NaN)`, _NaN) + test(`Math.pow(0, 0)`, 1) + test(`Math.pow(NaN, 0)`, 1) + test(`Math.pow(0, -0)`, 1) + test(`Math.pow(NaN, -0)`, 1) + test(`Math.pow(NaN, 1)`, _NaN) + test(`Math.pow(2, Infinity)`, _Infinity) + test(`1/Math.pow(2, -Infinity)`, _Infinity) + test(`Math.pow(1, Infinity)`, _NaN) + test(`Math.pow(1, -Infinity)`, _NaN) + test(`1/Math.pow(0.1, Infinity)`, _Infinity) + test(`Math.pow(0.1, -Infinity)`, _Infinity) + test(`Math.pow(Infinity, 1)`, _Infinity) + test(`1/Math.pow(Infinity, -1)`, _Infinity) + test(`Math.pow(-Infinity, 1)`, -_Infinity) + test(`Math.pow(-Infinity, 2)`, _Infinity) + test(`1/Math.pow(-Infinity, -1)`, -_Infinity) + test(`1/Math.pow(-Infinity, -2)`, _Infinity) + test(`1/Math.pow(0, 1)`, _Infinity) + test(`Math.pow(0, -1)`, _Infinity) + test(`1/Math.pow(-0, 1)`, -_Infinity) + test(`1/Math.pow(-0, 2)`, _Infinity) + test(`Math.pow(-0, -1)`, -_Infinity) + test(`Math.pow(-0, -2)`, _Infinity) + test(`Math.pow(-1, 0.1)`, _NaN) + + test(` + [ Math.pow(-1, +Infinity), Math.pow(1, Infinity) ]; + `, "NaN,NaN") + }) +} + +func TestMath_round(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.round(NaN)`, _NaN) + test(`1/Math.round(0)`, _Infinity) + test(`1/Math.round(-0)`, -_Infinity) + test(`Math.round(Infinity)`, _Infinity) + test(`Math.round(-Infinity)`, -_Infinity) + test(`1/Math.round(0.1)`, _Infinity) + test(`1/Math.round(-0.1)`, -_Infinity) + + test(`Math.round(3.5)`, 4) + test(`Math.round(-3.5)`, -3) + }) +} + +func TestMath_sin(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.sin(NaN)`, _NaN) + test(`1/Math.sin(+0)`, _Infinity) + test(`1/Math.sin(-0)`, -_Infinity) + test(`Math.sin(Infinity)`, _NaN) + test(`Math.sin(-Infinity)`, _NaN) + + test(`Math.sin(0.5)`, 0.479425538604203) + }) +} + +func TestMath_sqrt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.sqrt(NaN)`, _NaN) + test(`Math.sqrt(-1)`, _NaN) + test(`1/Math.sqrt(+0)`, _Infinity) + test(`1/Math.sqrt(-0)`, -_Infinity) + test(`Math.sqrt(Infinity)`, _Infinity) + + test(`Math.sqrt(2)`, 1.4142135623730951) + }) +} + +func TestMath_tan(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.tan(NaN)`, _NaN) + test(`1/Math.tan(+0)`, _Infinity) + test(`1/Math.tan(-0)`, -_Infinity) + test(`Math.tan(Infinity)`, _NaN) + test(`Math.tan(-Infinity)`, _NaN) + + test(`Math.tan(0.5)`, 0.5463024898437905) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/number_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/number_test.go new file mode 100644 index 000000000..f3790e077 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/number_test.go @@ -0,0 +1,167 @@ +package otto + +import ( + "testing" +) + +func TestNumber(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = Object.getOwnPropertyDescriptor(Number, "prototype"); + [ [ typeof Number.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestNumber_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + new Number(451).toString(); + `, "451") + + test(` + new Number(451).toString(10); + `, "451") + + test(` + new Number(451).toString(8); + `, "703") + + test(`raise: + new Number(451).toString(1); + `, "RangeError: RangeError: toString() radix must be between 2 and 36") + + test(`raise: + new Number(451).toString(Infinity); + `, "RangeError: RangeError: toString() radix must be between 2 and 36") + + test(` + new Number(NaN).toString() + `, "NaN") + + test(` + new Number(Infinity).toString() + `, "Infinity") + + test(` + new Number(Infinity).toString(16) + `, "Infinity") + + test(` + [ + Number.prototype.toString(undefined), + new Number().toString(undefined), + new Number(0).toString(undefined), + new Number(-1).toString(undefined), + new Number(1).toString(undefined), + new Number(Number.NaN).toString(undefined), + new Number(Number.POSITIVE_INFINITY).toString(undefined), + new Number(Number.NEGATIVE_INFINITY).toString(undefined) + ] + `, "0,0,0,-1,1,NaN,Infinity,-Infinity") + }) +} + +func TestNumber_toFixed(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toFixed(2)`, "451.00") + test(`12345.6789.toFixed()`, "12346") + test(`12345.6789.toFixed(1)`, "12345.7") + test(`12345.6789.toFixed(6)`, "12345.678900") + test(`(1.23e-20).toFixed(2)`, "0.00") + test(`2.34.toFixed(1)`, "2.3") // FIXME Wtf? "2.3" + test(`-2.34.toFixed(1)`, -2.3) // FIXME Wtf? -2.3 + test(`(-2.34).toFixed(1)`, "-2.3") + + test(`raise: + new Number("a").toFixed(Number.POSITIVE_INFINITY); + `, "RangeError: toFixed() precision must be between 0 and 20") + + test(` + [ + new Number(1e21).toFixed(), + new Number(1e21).toFixed(0), + new Number(1e21).toFixed(1), + new Number(1e21).toFixed(1.1), + new Number(1e21).toFixed(0.9), + new Number(1e21).toFixed("1"), + new Number(1e21).toFixed("1.1"), + new Number(1e21).toFixed("0.9"), + new Number(1e21).toFixed(Number.NaN), + new Number(1e21).toFixed("some string") + ]; + `, "1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21") + + test(`raise: + new Number(1e21).toFixed(Number.POSITIVE_INFINITY); + `, "RangeError: toFixed() precision must be between 0 and 20") + }) +} + +func TestNumber_toExponential(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toExponential(2)`, "4.51e+02") + test(`77.1234.toExponential()`, "7.71234e+01") + test(`77.1234.toExponential(4)`, "7.7123e+01") + test(`77.1234.toExponential(2)`, "7.71e+01") + test(`77 .toExponential()`, "7.7e+01") + }) +} + +func TestNumber_toPrecision(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toPrecision()`, "451") + test(`new Number(451).toPrecision(1)`, "5e+02") + test(`5.123456.toPrecision()`, "5.123456") + test(`5.123456.toPrecision(5)`, "5.1235") + test(`5.123456.toPrecision(2)`, "5.1") + test(`5.123456.toPrecision(1)`, "5") + }) +} + +func TestNumber_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + new Number(451).toLocaleString(), + new Number(451).toLocaleString(10), + new Number(451).toLocaleString(8) + ]; + `, "451,451,703") + }) +} + +func Test_toInteger(t *testing.T) { + tt(t, func() { + integer := toInteger(toValue(0.0)) + is(integer.valid(), true) + is(integer.exact(), true) + + integer = toInteger(toValue(3.14159)) + is(integer.valid(), true) + is(integer.exact(), false) + }) +} + +func Test_NaN(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ NaN === NaN, NaN == NaN ]; + `, "false,false") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/object.go b/Godeps/_workspace/src/github.com/obscuren/otto/object.go new file mode 100644 index 000000000..fe6f58729 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/object.go @@ -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) + if method.isCallable() { + result := method._object().Call(toValue_object(self)) + if result.IsPrimitive() { + return result + } + } + } + + panic(newTypeError()) + return UndefinedValue() +} + +func (self *_object) String() string { + return toString(self.DefaultValue(defaultValueHintString)) +} + +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 = UndefinedValue() + } + _, 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:]...) + } + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/object_class.go b/Godeps/_workspace/src/github.com/obscuren/otto/object_class.go new file mode 100644 index 000000000..0464503b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/object_class.go @@ -0,0 +1,484 @@ +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 UndefinedValue() +} + +// 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(newTypeError()) + } + } + + 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(newTypeError()) + } + + return false, nil, nil +} + +// 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 { + typeErrorResult(throw) + } else if setter != nil { + setter.callSet(toValue(self), value) + } 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) { + 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].callSet(toValue(self), value) + 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].callSet(toValue(self), value) + return + } + if throw { + panic(newTypeError()) + } + default: + panic(newTypeError()) + } + } +} + +// 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(newTypeError()) + } + 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 typeErrorResult(throw) +} + +func objectClone(self0 *_object, self1 *_object, clone *_clone) *_object { + *self1 = *self0 + + self1.runtime = clone.runtime + if self1.prototype != nil { + self1.prototype = clone.object(self0.prototype) + } + self1.property = make(map[string]_property, len(self0.property)) + self1.propertyOrder = make([]string, len(self0.propertyOrder)) + copy(self1.propertyOrder, self0.propertyOrder) + for index, property := range self0.property { + self1.property[index] = clone.property(property) + } + + switch value := self0.value.(type) { + case _functionObject: + self1.value = value.clone(clone) + case _argumentsObject: + self1.value = value.clone(clone) + } + + return self1 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/object_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/object_test.go new file mode 100644 index 000000000..d1e90680b --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/object_test.go @@ -0,0 +1,639 @@ +package otto + +import ( + "testing" +) + +func TestObject_(t *testing.T) { + tt(t, func() { + test, _ := test() + + object := newObject(nil, "") + is(object != nil, true) + + object.put("xyzzy", toValue("Nothing happens."), true) + is(object.get("xyzzy"), "Nothing happens.") + + test(` + var abc = Object.getOwnPropertyDescriptor(Object, "prototype"); + [ [ typeof Object.prototype, abc.writable, abc.enumerable, abc.configurable ], + ]; + `, "object,false,false,false") + }) +} + +func TestStringObject(t *testing.T) { + tt(t, func() { + object := New().runtime.newStringObject(toValue("xyzzy")) + is(object.get("1"), "y") + is(object.get("10"), "undefined") + is(object.get("2"), "z") + }) +} + +func TestObject_getPrototypeOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = {}; + def = Object.getPrototypeOf(abc); + ghi = Object.getPrototypeOf(def); + [abc,def,ghi,ghi+""]; + `, "[object Object],[object Object],,null") + + test(` + abc = Object.getOwnPropertyDescriptor(Object, "getPrototypeOf"); + [ abc.value === Object.getPrototypeOf, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + }) +} + +func TestObject_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ new Object("abc"), new Object(2+2) ]; + `, "abc,4") + }) +} + +func TestObject_create(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.create()`, "TypeError") + + test(` + var abc = Object.create(null) + var def = Object.create({x: 10, y: 20}) + var ghi = Object.create(Object.prototype) + + var jkl = Object.create({x: 10, y: 20}, { + z: { + value: 30, + writable: true + }, + // sum: { + // get: function() { + // return this.x + this.y + this.z + // } + // } + }); + [ abc.prototype, def.x, def.y, ghi, jkl.x, jkl.y, jkl.z ] + `, ",10,20,[object Object],10,20,30") + + test(` + var properties = {}; + Object.defineProperty(properties, "abc", { + value: {}, + enumerable: false + }); + var mno = Object.create({}, properties); + mno.hasOwnProperty("abc"); + `, false) + }) +} + +func TestObject_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + ({}).toLocaleString(); + `, "[object Object]") + + test(` + object = { + toString: function() { + return "Nothing happens."; + } + }; + object.toLocaleString(); + `, "Nothing happens.") + }) +} + +func TestObject_isExtensible(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.isExtensible(); + `, "TypeError") + + // FIXME terst, Why raise? + test(`raise: + Object.isExtensible({}); + `, true) + + test(`Object.isExtensible.length`, 1) + test(`Object.isExtensible.prototype`, "undefined") + }) +} + +func TestObject_preventExtensions(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.preventExtensions() + `, "TypeError") + + test(`raise: + var abc = { def: true }; + var ghi = Object.preventExtensions(abc); + [ ghi.def === true, Object.isExtensible(abc), Object.isExtensible(ghi) ]; + `, "true,false,false") + + test(` + var abc = new String(); + var def = Object.isExtensible(abc); + Object.preventExtensions(abc); + var ghi = false; + try { + Object.defineProperty(abc, "0", { value: "~" }); + } catch (err) { + ghi = err instanceof TypeError; + } + [ def, ghi, abc.hasOwnProperty("0"), typeof abc[0] ]; + `, "true,true,false,undefined") + + test(`Object.preventExtensions.length`, 1) + test(`Object.preventExtensions.prototype`, "undefined") + }) +} + +func TestObject_isSealed(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.isSealed.length`, 1) + test(`Object.isSealed.prototype`, "undefined") + }) +} + +func TestObject_seal(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.seal()`, "TypeError") + + test(` + var abc = {a:1,b:1,c:3}; + var sealed = Object.isSealed(abc); + Object.seal(abc); + [sealed, Object.isSealed(abc)]; + `, "false,true") + + test(` + var abc = {a:1,b:1,c:3}; + var sealed = Object.isSealed(abc); + var caught = false; + Object.seal(abc); + abc.b = 5; + Object.defineProperty(abc, "a", {value:4}); + try { + Object.defineProperty(abc, "a", {value:42,enumerable:false}); + } catch (e) { + caught = e instanceof TypeError; + } + [sealed, Object.isSealed(abc), caught, abc.a, abc.b]; + `, "false,true,true,4,5") + + test(`Object.seal.length`, 1) + test(`Object.seal.prototype`, "undefined") + }) +} + +func TestObject_isFrozen(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.isFrozen()`, "TypeError") + test(`Object.isFrozen(Object.preventExtensions({a:1}))`, false) + test(`Object.isFrozen({})`, false) + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + value: "def", + writable: true, + configurable: false + }); + Object.preventExtensions(abc); + !Object.isFrozen(abc); + `, true) + + test(`Object.isFrozen.length`, 1) + test(`Object.isFrozen.prototype`, "undefined") + }) +} + +func TestObject_freeze(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.freeze()`, "TypeError") + + test(` + var abc = {a:1,b:2,c:3}; + var frozen = Object.isFrozen(abc); + Object.freeze(abc); + abc.b = 5; + [frozen, Object.isFrozen(abc), abc.b]; + `, "false,true,2") + + test(` + var abc = {a:1,b:2,c:3}; + var frozen = Object.isFrozen(abc); + var caught = false; + Object.freeze(abc); + abc.b = 5; + try { + Object.defineProperty(abc, "a", {value:4}); + } catch (e) { + caught = e instanceof TypeError; + } + [frozen, Object.isFrozen(abc), caught, abc.a, abc.b]; + `, "false,true,true,1,2") + + test(`Object.freeze.length`, 1) + test(`Object.freeze.prototype`, "undefined") + }) +} + +func TestObject_defineProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(abc, def, ghi){ + Object.defineProperty(arguments, "0", { + enumerable: false + }); + return true; + })(0, 1, 2); + `, true) + + test(` + var abc = {}; + abc.def = 3.14; // Default: writable: true, enumerable: true, configurable: true + + Object.defineProperty(abc, "def", { + value: 42 + }); + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ ghi.value, ghi.writable, ghi.enumerable, ghi.configurable ]; + `, "42,true,true,true") + + // Test that we handle the case of DefineOwnProperty + // where [[Writable]] is something but [[Value]] is not + test(` + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + 0 in abc; + `, true) + + // Test that we handle the case of DefineOwnProperty + // where [[Writable]] is something but [[Value]] is not + // (and the property originally had something for [[Value]] + test(` + abc = { + def: 42 + }; + Object.defineProperty(abc, "def", { writable: false }); + abc.def; + `, 42) + }) +} + +func TestObject_keys(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.keys({ abc:undefined, def:undefined })`, "abc,def") + + test(` + function abc() { + this.abc = undefined; + this.def = undefined; + } + Object.keys(new abc()) + `, "abc,def") + + test(` + function def() { + this.ghi = undefined; + } + def.prototype = new abc(); + Object.keys(new def()); + `, "ghi") + + test(` + var ghi = Object.create( + { + abc: undefined, + def: undefined + }, + { + ghi: { value: undefined, enumerable: true }, + jkl: { value: undefined, enumerable: false } + } + ); + Object.keys(ghi); + `, "ghi") + + test(` + (function(abc, def, ghi){ + return Object.keys(arguments) + })(undefined, undefined); + `, "0,1") + + test(` + (function(abc, def, ghi){ + return Object.keys(arguments) + })(undefined, undefined, undefined, undefined); + `, "0,1,2,3") + }) +} + +func TestObject_getOwnPropertyNames(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.getOwnPropertyNames({ abc:undefined, def:undefined })`, "abc,def") + + test(` + var ghi = Object.create( + { + abc: undefined, + def: undefined + }, + { + ghi: { value: undefined, enumerable: true }, + jkl: { value: undefined, enumerable: false } + } + ); + Object.getOwnPropertyNames(ghi) + `, "ghi,jkl") + }) +} + +func TestObjectGetterSetter(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.create({}, { + abc: { + get: function(){ + return "true"; + }, + writable: true + } + }).abc; + `, "TypeError") + + test(`raise: + Object.create({}, { + abc: { + get: function(){ + return "true"; + }, + writable: false + } + }).abc; + `, "TypeError") + + test(` + Object.create({}, { + abc: { + get: function(){ + return "true"; + } + } + }).abc; + `, "true") + + test(` + Object.create({xyz:true},{abc:{get:function(){return this.xyx}}}).abc; + Object.create({ + xyz: true + }, { + abc: { + get: function(){ + return this.xyz; + } + } + }).abc; + `, true) + + test(` + var abc = false; + var def = Object.create({}, { + xyz: { + set: function(value) { + abc = value; + } + } + }); + def.xyz = true; + [ abc ]; + `, "true") + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + value: "xyzzy", + configurable: true + }); + Object.preventExtensions(abc); + Object.defineProperty(abc, "def", { + get: function() { + return 5; + } + }); + var def = Object.getOwnPropertyDescriptor(abc, "def"); + [ abc.def, typeof def.get, typeof def.set, typeof def.value, def.configurable, def.enumerable, typeof def.writable ]; + `, "5,function,undefined,undefined,true,false,undefined") + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + get: function() { + return 5; + } + configurable: true + }); + Object.preventExtensions(abc); + Object.defineProperty(abc, "def", { + value: "xyzzy", + }); + var def = Object.getOwnPropertyDescriptor(abc, "def"); + [ abc.def, typeof def.get, typeof def.set, def.value, def.configurable, def.enumerable, def.writable ]; + `, "xyzzy,undefined,undefined,xyzzy,true,false,false") + + test(` + var abc = {}; + + function _get0() { + return 10; + } + + function _set(value) { + abc.def = value; + } + + Object.defineProperty(abc, "ghi", { + get: _get0, + set: _set, + configurable: true + }); + + function _get1() { + return 20; + } + + Object.defineProperty(abc, "ghi", { + get: _get0 + }); + + var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); + [ typeof descriptor.set ]; + `, "function") + + test(`raise: + var abc = []; + Object.defineProperty(abc, "length", { + get: function () { + return 2; + } + }); + `, "TypeError") + + test(` + var abc = {}; + + var getter = function() { + return 1; + } + + Object.defineProperty(abc, "def", { + get: getter, + configurable: false + }); + + var jkl = undefined; + try { + Object.defineProperty(abc, "def", { + get: undefined + }); + } + catch (err) { + jkl = err; + } + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ jkl instanceof TypeError, ghi.get === getter, ghi.configurable, ghi.enumerable ]; + `, "true,true,false,false") + + test(` + var abc = {}; + + var getter = function() { + return 1; + }; + + Object.defineProperty(abc, "def", { + get: getter + }); + + Object.defineProperty(abc, "def", { + set: undefined + }); + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ ghi.get === getter, ghi.set === undefined, ghi.configurable, ghi.enumerable ]; + `, "true,true,false,false") + + test(` + var abc = {}; + + var getter = function() { + return 1; + }; + + Object.defineProperty(abc, "def", { + get: getter + }); + + var jkl = undefined; + try { + Object.defineProperty(abc, "def", { + set: function() {} + }); + } + catch (err) { + jkl = err; + } + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ jkl instanceof TypeError, ghi.get === getter, ghi.set, ghi.configurable, ghi.enumerable ]; + `, "true,true,,false,false") + + test(` + var abc = {}; + var def = "xyzzy"; + + Object.defineProperty(abc, "ghi", { + get: undefined, + set: function(value) { + def = value; + }, + enumerable: true, + configurable: true + }); + + var hasOwn = abc.hasOwnProperty("ghi"); + var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); + + [ hasOwn, typeof descriptor.get ]; + `, "true,undefined") + + test(` + var abc = "xyzzy"; + Object.defineProperty(Array.prototype, "abc", { + get: function () { + return abc; + }, + set: function (value) { + abc = value; + }, + enumerable: true, + configurable: true + }); + var def = []; + def.abc = 3.14159; + [ def.hasOwnProperty("abc"), def.abc, abc ]; + `, "false,3.14159,3.14159") + }) +} + +func TestProperty(t *testing.T) { + tt(t, func() { + property := _property{} + property.writeOn() + is(property.writeSet(), true) + + property.writeClear() + is(property.writeSet(), false) + + property.writeOff() + is(property.writeSet(), true) + + property.writeClear() + is(property.writeSet(), false) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto.go b/Godeps/_workspace/src/github.com/obscuren/otto/otto.go new file mode 100644 index 000000000..1cb79cb0a --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto.go @@ -0,0 +1,561 @@ +/* +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.UndefinedValue() + }) + +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. + * Error reporting needs to be improved. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + +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()) + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + vm.Run(unsafe) // Here be dragons (risky code) + vm.Interrupt = nil + } + +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/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.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) + 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) { + return self.runtime.cmpl_run(src) +} + +// 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 := UndefinedValue() + err := catchPanic(func() { + value = self.getValue(name) + }) + return value, err +} + +func (self Otto) getValue(name string) Value { + return self.runtime.GlobalEnvironment.GetValue(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.GlobalEnvironment.SetValue(name, value, false) +} + +// 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 := UndefinedValue() + + construct := false + if strings.HasPrefix(source, "new ") { + source = source[4:] + construct = true + } + + if !construct && this == nil { + program, err := self.runtime.cmpl_parse("", source+"()") + 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 UndefinedValue(), err + } + return value, nil + } + } + } + } else { + value, err := self.ToValue(this) + if err != nil { + return UndefinedValue(), err + } + thisValue = value + } + + { + this := thisValue + + fn, err := self.Run(source) + if err != nil { + return UndefinedValue(), err + } + + if construct { + result, err := fn.constructSafe(this, argumentList...) + if err != nil { + return UndefinedValue(), err + } + return result, nil + } + + result, err := fn.Call(this, argumentList...) + if err != nil { + return UndefinedValue(), 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) + 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.ToValue(value) +} + +// Copy will create a copy/clone of the runtime. +// +// Copy is useful for saving some processing time when creating many similar +// runtimes. +// +// This implementation is alpha-ish, and works by introspecting every part of the runtime +// and reallocating and then relinking everything back together. Please report if you +// notice any inadvertent sharing of data between copies. +func (self *Otto) Copy() *Otto { + otto := &Otto{ + runtime: self.runtime.clone(), + } + otto.runtime.Otto = otto + return otto +} + +// 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 UndefinedValue(), 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 := UndefinedValue() + err := catchPanic(func() { + value = self.object.get(name) + }) + 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.ToValue(value) + if err != nil { + return err + } + err = catchPanic(func() { + self.object.put(name, value, true) + }) + return err + } +} + +// Get the keys for the 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 +} + +// 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/otto/Makefile new file mode 100644 index 000000000..bac5cd4a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto/Makefile @@ -0,0 +1,5 @@ +.PHONY: build + +build: + go build -a + -gxc build-darwin-386 -a diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto/main.go b/Godeps/_workspace/src/github.com/obscuren/otto/otto/main.go new file mode 100644 index 000000000..0dcc91e51 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + + "github.com/robertkrimen/otto" + "github.com/robertkrimen/otto/underscore" +) + +var flag_underscore *bool = flag.Bool("underscore", true, "Load underscore into the runtime environment") + +func readSource(filename string) ([]byte, error) { + if filename == "" || filename == "-" { + return ioutil.ReadAll(os.Stdin) + } + return ioutil.ReadFile(filename) +} + +func main() { + flag.Parse() + + if !*flag_underscore { + underscore.Disable() + } + + err := func() error { + src, err := readSource(flag.Arg(0)) + if err != nil { + return err + } + + vm := otto.New() + _, err = vm.Run(src) + return err + }() + if err != nil { + fmt.Println(err) + os.Exit(64) + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto_.go b/Godeps/_workspace/src/github.com/obscuren/otto/otto_.go new file mode 100644 index 000000000..e9ff66606 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto_.go @@ -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{toValue(arguments[0])} + } + + valueArray := make([]Value, length) + for index, value := range arguments { + valueArray[index] = 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 UndefinedValue(), 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 := toInteger(indexValue).value + 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 = toInteger(lengthValue).value + } + 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) + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto_error_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/otto_error_test.go new file mode 100644 index 000000000..90863ca12 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto_error_test.go @@ -0,0 +1,48 @@ +package otto + +import ( + "testing" +) + +func TestOttoError(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(`throw "Xyzzy"`) + is(err, "Xyzzy") + + _, err = vm.Run(`throw new TypeError()`) + is(err, "TypeError") + + _, err = vm.Run(`throw new TypeError("Nothing happens.")`) + is(err, "TypeError: Nothing happens.") + + _, err = ToValue([]byte{}) + is(err, "TypeError: Invalid value (slice): Missing runtime: [] ([]uint8)") + + _, err = vm.Run(` + (function(){ + return abcdef.length + })() + `) + is(err, "ReferenceError: abcdef is not defined") + + _, err = vm.Run(` + function start() { + } + + start() + + xyzzy() + `) + is(err, "ReferenceError: xyzzy is not defined") + + _, err = vm.Run(` + // Just a comment + + xyzzy + `) + is(err, "ReferenceError: xyzzy is not defined") + + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/otto_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/otto_test.go new file mode 100644 index 000000000..bacdda724 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/otto_test.go @@ -0,0 +1,1331 @@ +package otto + +import ( + "bytes" + "io" + "testing" + + "github.com/robertkrimen/otto/parser" +) + +func TestOtto(t *testing.T) { + tt(t, func() { + test, _ := test() + + test("xyzzy = 2", 2) + + test("xyzzy + 2", 4) + + test("xyzzy += 16", 18) + + test("xyzzy", 18) + + test(` + (function(){ + return 1 + })() + `, 1) + + test(` + (function(){ + return 1 + }).call(this) + `, 1) + + test(` + (function(){ + var result + (function(){ + result = -1 + })() + return result + })() + `, -1) + + test(` + var abc = 1 + abc || (abc = -1) + abc + `, 1) + + test(` + var abc = (function(){ 1 === 1 })(); + abc; + `, "undefined") + }) +} + +func TestFunction__(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + function abc() { + return 1; + }; + abc(); + `, 1) + }) +} + +func TestIf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = undefined; + def = undefined; + if (true) abc = 1 + else abc = 2; + if (false) { + def = 3; + } + else def = 4; + + [ abc, def ]; + `, "1,4") + + test(` + if (1) { + abc = 1; + } + else { + abc = 0; + } + abc; + `, 1) + + test(` + if (0) { + abc = 1; + } + else { + abc = 0; + } + abc; + `, 0) + + test(` + abc = 0; + if (0) { + abc = 1; + } + abc; + `, 0) + + test(` + abc = 0; + if (abc) { + abc = 1; + } + abc; + `, 0) + }) +} + +func TestSequence(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + 1, 2, 3; + `, 3) + }) +} + +func TestCall(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + Math.pow(3, 2); + `, 9) + }) +} + +func TestMember(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [ 0, 1, 2 ]; + def = { + "abc": 0, + "def": 1, + "ghi": 2, + }; + [ abc[2], def.abc, abc[1], def.def ]; + `, "2,0,1,1") + }) +} + +func Test_this(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + typeof this; + `, "object") + }) +} + +func TestWhile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4 + abc = 0 + while (limit) { + abc = abc + 1 + limit = limit - 1 + } + abc; + `, 4) + }) +} + +func TestSwitch_break(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = true; + var ghi = "Xyzzy"; + while (abc) { + switch ('def') { + case 'def': + break; + } + ghi = "Nothing happens."; + abc = false; + } + ghi; + `, "Nothing happens.") + + test(` + var abc = true; + var ghi = "Xyzzy"; + WHILE: + while (abc) { + switch ('def') { + case 'def': + break WHILE; + } + ghi = "Nothing happens." + abc = false + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + FOR: + for (;;) { + switch ('def') { + case 'def': + break FOR; + ghi = ""; + } + ghi = "Nothing happens."; + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + FOR: + for (var jkl in {}) { + switch ('def') { + case 'def': + break FOR; + ghi = "Something happens."; + } + ghi = "Nothing happens."; + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + function jkl() { + switch ('def') { + case 'def': + break; + ghi = ""; + } + ghi = "Nothing happens."; + } + while (abc) { + jkl(); + abc = false; + ghi = "Something happens."; + } + ghi; + `, "Something happens.") + }) +} + +func TestTryFinally(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + try { + abc = 1; + } + finally { + abc = 2; + } + abc; + `, 2) + + test(` + var abc = false, def = 0; + do { + def += 1; + if (def > 100) { + break; + } + try { + continue; + } + finally { + abc = true; + } + } + while(!abc && def < 10) + def; + `, 1) + + test(` + var abc = false, def = 0, ghi = 0; + do { + def += 1; + if (def > 100) { + break; + } + try { + throw 0; + } + catch (jkl) { + continue; + } + finally { + abc = true; + ghi = 11; + } + ghi -= 1; + } + while(!abc && def < 10) + ghi; + `, 11) + + test(` + var abc = 0, def = 0; + do { + try { + abc += 1; + throw "ghi"; + } + finally { + def = 1; + continue; + } + def -= 1; + } + while (abc < 2) + [ abc, def ]; + `, "2,1") + }) +} + +func TestTryCatch(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 1; + try { + throw 4; + abc = -1; + } + catch (xyzzy) { + abc += xyzzy + 1; + } + abc; + `, 6) + + test(` + abc = 1; + var def; + try { + try { + throw 4; + abc = -1; + } + catch (xyzzy) { + abc += xyzzy + 1; + throw 64; + } + } + catch (xyzzy) { + def = xyzzy; + abc = -2; + } + [ def, abc ]; + `, "64,-2") + }) +} + +func TestWith(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var def; + with({ abc: 9 }) { + def = abc; + } + def; + `, 9) + + test(` + var def; + with({ abc: function(){ + return 11; + } }) { + def = abc(); + } + def; + `, 11) + }) +} + +func TestSwitch(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + switch (0) { + default: + abc += 1; + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 15) + + test(` + abc = 0; + switch (3) { + default: + abc += 1; + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 8) + + test(` + abc = 0; + switch (60) { + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 0) + }) +} + +func TestForIn(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + for (property in { a: 1 }) { + abc = property; + } + abc; + `, "a") + + test(` + var ghi; + for (property in new String("xyzzy")) { + ghi = property; + } + ghi; + `, "4") + }) +} + +func TestFor(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 7; + for (i = 0; i < 3; i += 1) { + abc += 1; + } + abc; + `, 10) + + test(` + abc = 7; + for (i = 0; i < 3; i += 1) { + abc += 1; + if (i == 1) { + break; + } + } + abc; + `, 9) + + test(` + abc = 7; + for (i = 0; i < 3; i += 1) { + if (i == 2) { + continue; + } + abc += 1; + } + abc; + `, 9) + + test(` + abc = 0; + for (;;) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + + test(` + for (abc = 0; ;) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + + test(` + for (abc = 0; ; abc += 1) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + }) +} + +func TestLabelled(t *testing.T) { + tt(t, func() { + test, _ := test() + + // TODO Add emergency break + + test(` + xyzzy: for (var abc = 0; abc <= 0; abc++) { + for (var def = 0; def <= 1; def++) { + if (def === 0) { + continue xyzzy; + } else { + } + } + } + `) + + test(` + abc = 0 + def: + while (true) { + while (true) { + abc = abc + 1 + if (abc > 11) { + break def; + } + } + } + abc; + `, 12) + + test(` + abc = 0 + def: + do { + do { + abc = abc + 1 + if (abc > 11) { + break def; + } + } while (true) + } while (true) + abc; + `, 12) + }) +} + +func TestConditional(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ true ? false : true, true ? 1 : 0, false ? 3.14159 : "abc" ]; + `, "false,1,abc") + }) +} + +func TestArrayLiteral(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ 1, , 3.14159 ]; + `, "1,,3.14159") + }) +} + +func TestAssignment(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 1; + abc; + `, 1) + + test(` + abc += 2; + abc; + `, 3) + }) +} + +func TestBinaryOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`0 == 1`, false) + test(`1 == "1"`, true) + test(`0 === 1`, false) + test(`1 === "1"`, false) + test(`"1" === "1"`, true) + }) +} + +func Test_typeof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`typeof abc`, "undefined") + test(`typeof abc === 'undefined'`, true) + test(`typeof {}`, "object") + test(`typeof null`, "object") + }) +} + +func Test_PrimitiveValueObjectValue(t *testing.T) { + tt(t, func() { + test, _ := test() + + Number11 := test(`new Number(11)`) + is(toFloat(Number11), 11) + }) +} + +func Test_eval(t *testing.T) { + tt(t, func() { + test, _ := test() + + // FIXME terst, Is this correct? + test(` + var abc = 1; + `, "undefined") + + test(` + eval("abc += 1"); + `, 2) + + test(` + (function(){ + var abc = 11; + eval("abc += 1"); + return abc; + })(); + `, 12) + test(`abc`, 2) + + test(` + (function(){ + try { + eval("var prop = \\u2029;"); + return false; + } catch (abc) { + return [ abc instanceof SyntaxError, abc.toString() ]; + } + })(); + `, "true,SyntaxError: Unexpected token ILLEGAL") + + test(` + function abc(){ + this.THIS = eval("this"); + } + var def = new abc(); + def === def.THIS; + `, true) + }) +} + +func Test_evalDirectIndirect(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = "global"; + (function(){ + try { + var _eval = eval; + var abc = "function"; + if ( + _eval("\'global\' === abc") === true && // eval (Indirect) + eval("\'function\' === abc") === true // eval (Direct) + ) { + return true; + } + return false; + } finally { + delete this.abc; + } + })() + `, true) + }) +} + +func TestError_URIError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new URIError() instanceof URIError`, true) + + test(` + var abc + try { + decodeURI("http://example.com/ _^#%") + } + catch (def) { + abc = def instanceof URIError + } + abc + `, true) + }) +} + +func TestTo(t *testing.T) { + tt(t, func() { + test, _ := test() + + { + value, _ := test(`"11"`).ToFloat() + is(value, float64(11)) + } + + { + value, _ := test(`"11"`).ToInteger() + is(value, int64(11)) + + value, _ = test(`1.1`).ToInteger() + is(value, int64(1)) + } + }) +} + +func TestShouldError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + xyzzy + throw new TypeError("Nothing happens.") + `, "ReferenceError: xyzzy is not defined") + }) +} + +func TestAPI(t *testing.T) { + tt(t, func() { + test, vm := test() + + test(` + String.prototype.xyzzy = function(){ + return this.length + 11 + (arguments[0] || 0) + } + abc = new String("xyzzy") + def = "Nothing happens." + abc.xyzzy() + `, 16) + abc, _ := vm.Get("abc") + def, _ := vm.Get("def") + object := abc.Object() + result, _ := object.Call("xyzzy") + is(result, 16) + result, _ = object.Call("xyzzy", 1) + is(result, 17) + value, _ := object.Get("xyzzy") + result, _ = value.Call(def) + is(result, 27) + result, _ = value.Call(def, 3) + is(result, 30) + object = value.Object() // Object xyzzy + result, _ = object.Value().Call(def, 3) + is(result, 30) + + test(` + abc = { + 'abc': 1, + 'def': false, + 3.14159: NaN, + }; + abc['abc']; + `, 1) + abc, err := vm.Get("abc") + is(err, nil) + object = abc.Object() // Object abc + value, err = object.Get("abc") + is(err, nil) + is(value, 1) + is(object.Keys(), []string{"abc", "def", "3.14159"}) + + test(` + abc = [ 0, 1, 2, 3.14159, "abc", , ]; + abc.def = true; + `) + abc, err = vm.Get("abc") + is(err, nil) + object = abc.Object() // Object abc + is(object.Keys(), []string{"0", "1", "2", "3", "4", "def"}) + }) +} + +func TestUnicode(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`var abc = eval("\"a\uFFFFa\"");`, "undefined") + + test(`abc.length`, 3) + + test(`abc != "aa"`, true) + + test("abc[1] === \"\uFFFF\"", true) + }) +} + +func TestDotMember(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = { + ghi: 11, + } + abc.def = "Xyzzy" + abc.null = "Nothing happens." + `) + test(`abc.def`, "Xyzzy") + test(`abc.null`, "Nothing happens.") + test(`abc.ghi`, 11) + + test(` + abc = { + null: 11, + } + `) + test(`abc.def`, "undefined") + test(`abc.null`, 11) + test(`abc.ghi`, "undefined") + }) +} + +func Test_stringToFloat(t *testing.T) { + tt(t, func() { + + is(stringToFloat("10e10000"), _Infinity) + is(stringToFloat("10e10_."), _NaN) + }) +} + +func Test_delete(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + delete 42; + `, true) + + test(` + var abc = delete $_undefined_$; + abc = abc && delete ($_undefined_$); + abc; + `, true) + + // delete should not trigger get() + test(` + var abc = { + get def() { + throw "Test_delete: delete should not trigger get()" + } + }; + delete abc.def + `, true) + }) +} + +func TestObject_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var object = {}; + + var descriptor = new Boolean(false); + descriptor.configurable = true; + + Object.defineProperties(object, { + property: descriptor + }); + + var abc = object.hasOwnProperty("property"); + delete object.property; + var def = object.hasOwnProperty("property"); + + [ abc, def ]; + `, "true,false") + + test(` + var object = [0, 1, 2]; + Object.defineProperty(object, "0", { + value: 42, + writable: false, + enumerable: false, + configurable: false + }); + var abc = Object.getOwnPropertyDescriptor(object, "0"); + [ abc.value, abc.writable, abc.enumerable, abc.configurable ]; + `, "42,false,false,false") + + test(` + var abc = { "xyzzy": 42 }; + var def = Object.defineProperties(abc, ""); + abc === def; + `, true) + }) +} + +func Test_assignmentEvaluationOrder(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + ((abc = 1) & abc); + `, 1) + + test(` + var abc = 0; + (abc & (abc = 1)); + `, 0) + }) +} + +func TestOttoCall(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(` + var abc = { + ghi: 1, + def: function(def){ + var ghi = 0; + if (this.ghi) { + ghi = this.ghi; + } + return "def: " + (def + 3.14159 + ghi); + } + }; + `) + is(err, nil) + + value, err := vm.Call(`abc.def`, nil, 2) + is(err, nil) + is(value, "def: 6.14159") + + value, err = vm.Call(`abc.def`, "", 2) + is(err, nil) + is(value, "def: 5.14159") + + // Do not attempt to do a ToValue on a this of nil + value, err = vm.Call(`jkl.def`, nil, 1, 2, 3) + is(err, "!=", nil) + is(value, "undefined") + + value, err = vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") + is(err, nil) + is(value, "1,2,3,,4,5,6,7,abc") + }) +} + +func TestOttoCall_new(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + value, err := call.Otto.Call(`new Object`, nil, "Nothing happens.") + is(err, nil) + return value + }) + test(` + def = abc(); + [ def, def instanceof String ]; + `, "Nothing happens.,true") + }) +} + +func TestOttoCall_throw(t *testing.T) { + // FIXME? (Been broken for a while) + // Looks like this has been broken for a while... what + // behavior do we want here? + + return + + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + if false { + call.Otto.Call(`throw eval`, nil, "({ def: 3.14159 })") + } + call.Otto.Call(`throw Error`, nil, "abcdef") + return UndefinedValue() + }) + // TODO try { abc(); } catch (err) { error = err } + // Possible unrelated error case: + // If error is not declared beforehand, is later referencing it a ReferenceError? + // Should the catch { } declare error in the outer scope? + test(` + var error; + try { + abc(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def ]; + `, "true,abcdef,") + + vm.Set("def", func(call FunctionCall) Value { + call.Otto.Call(`throw new Object`, nil, 3.14159) + return UndefinedValue() + }) + test(` + try { + def(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def, typeof error, error, error instanceof Number ]; + `, "false,,,object,3.14159,true") + }) +} + +func TestOttoCopy(t *testing.T) { + tt(t, func() { + vm0 := New() + vm0.Run(` + var abc = function() { + return "Xyzzy"; + }; + + function def() { + return abc() + (0 + {}); + } + `) + + value, err := vm0.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + + vm1 := vm0.Copy() + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + + vm1.Run(` + abc = function() { + return 3.14159; + }; + `) + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "3.141590[object Object]") + + value, err = vm0.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + }) +} + +func TestOttoCall_clone(t *testing.T) { + tt(t, func() { + vm := New().clone() + rt := vm.runtime + + { + // FIXME terst, Check how this comparison is done + is(rt.Global.Array.prototype, rt.Global.FunctionPrototype) + is(rt.Global.ArrayPrototype, "!=", nil) + is(rt.Global.Array.runtime, rt) + is(rt.Global.Array.prototype.runtime, rt) + is(rt.Global.Array.get("prototype")._object().runtime, rt) + } + + { + value, err := vm.Run(`[ 1, 2, 3 ].toString()`) + is(err, nil) + is(value, "1,2,3") + } + + { + value, err := vm.Run(`[ 1, 2, 3 ]`) + is(err, nil) + is(value, "1,2,3") + object := value._object() + is(object, "!=", nil) + is(object.prototype, rt.Global.ArrayPrototype) + + value, err = vm.Run(`Array.prototype`) + is(err, nil) + object = value._object() + is(object.runtime, rt) + is(object, "!=", nil) + is(object, rt.Global.ArrayPrototype) + } + + { + otto1 := New() + _, err := otto1.Run(` + var abc = 1; + var def = 2; + `) + is(err, nil) + + otto2 := otto1.clone() + value, err := otto2.Run(`abc += 1; abc;`) + is(err, nil) + is(value, 2) + + value, err = otto1.Run(`abc += 4; abc;`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = 1; + var def = function(value) { + abc += value; + return abc; + } + `) + is(err, nil) + + vm2 := vm1.clone() + value, err := vm2.Run(`def(1)`) + is(err, nil) + is(value, 2) + + value, err = vm1.Run(`def(4)`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = { + ghi: 1, + jkl: function(value) { + this.ghi += value; + return this.ghi; + } + }; + var def = { + abc: abc + }; + `) + is(err, nil) + + otto2 := vm1.clone() + value, err := otto2.Run(`def.abc.jkl(1)`) + is(err, nil) + is(value, 2) + + value, err = vm1.Run(`def.abc.jkl(4)`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = function() { return "abc"; }; + var def = function() { return "def"; }; + `) + is(err, nil) + + vm2 := vm1.clone() + value, err := vm2.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "def"; }`) + + _, err = vm2.Run(` + var def = function() { return "ghi"; }; + `) + is(err, nil) + + value, err = vm1.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "def"; }`) + + value, err = vm2.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "ghi"; }`) + } + + }) +} + +func TestOttoRun(t *testing.T) { + tt(t, func() { + vm := New() + + program, err := parser.ParseFile(nil, "", "", 0) + is(err, nil) + value, err := vm.Run(program) + is(err, nil) + is(value, UndefinedValue()) + + program, err = parser.ParseFile(nil, "", "2 + 2", 0) + is(err, nil) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + + program, err = parser.ParseFile(nil, "", "var abc; if (!abc) abc = 0; abc += 2; abc;", 0) + value, err = vm.Run(program) + is(err, nil) + is(value, 2) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + value, err = vm.Run(program) + is(err, nil) + is(value, 6) + + { + src := []byte("var abc; if (!abc) abc = 0; abc += 2; abc;") + value, err = vm.Run(src) + is(err, nil) + is(value, 8) + + value, err = vm.Run(bytes.NewBuffer(src)) + is(err, nil) + is(value, 10) + + value, err = vm.Run(io.Reader(bytes.NewBuffer(src))) + is(err, nil) + is(value, 12) + } + + { + script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + is(err, nil) + + value, err = vm.Run(script) + is(err, nil) + is(value, 14) + + value, err = vm.Run(script) + is(err, nil) + is(value, 16) + + is(script.String(), "// \nvar abc; if (!abc) abc = 0; abc += 2; abc;") + } + }) +} + +func Test_objectLength(t *testing.T) { + tt(t, func() { + _, vm := test() + + value := vm.Set("abc", []string{"jkl", "mno"}) + is(objectLength(value._object()), 2) + + value, _ = vm.Run(`[1, 2, 3]`) + is(objectLength(value._object()), 3) + + value, _ = vm.Run(`new String("abcdefghi")`) + is(objectLength(value._object()), 9) + + value, _ = vm.Run(`"abcdefghi"`) + is(objectLength(value._object()), 0) + }) +} + +func BenchmarkNew(b *testing.B) { + for i := 0; i < b.N; i++ { + New() + } +} + +func BenchmarkClone(b *testing.B) { + vm := New() + b.ResetTimer() + for i := 0; i < b.N; i++ { + vm.clone() + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/panic_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/panic_test.go new file mode 100644 index 000000000..06f0a64fc --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/panic_test.go @@ -0,0 +1,40 @@ +package otto + +import ( + "testing" +) + +func Test_panic(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Test that property.value is set to something if writable is set + // to something + test(` + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + Object.defineProperty(abc, "0", { writable: false }); + "0" in abc; + `, true) + + test(`raise: + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + Object.defineProperty(abc, "0", { value: false, writable: false }); + `, "TypeError") + + // Test that a regular expression can contain \c0410 (CYRILLIC CAPITAL LETTER A) + // without panicking + test(` + var abc = 0x0410; + var def = String.fromCharCode(abc); + new RegExp("\\c" + def).exec(def); + `, "null") + + // Test transforming a transformable regular expression without a panic + test(` + new RegExp("\\u0000"); + new RegExp("\\undefined").test("undefined"); + `, true) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/parser/Makefile new file mode 100644 index 000000000..766fd4d0b --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/Makefile @@ -0,0 +1,4 @@ +.PHONY: test + +test: + go test diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/parser/README.markdown new file mode 100644 index 000000000..c3cae5b60 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/README.markdown @@ -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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/dbg.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/dbg.go new file mode 100644 index 000000000..3c5f2f698 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/dbg.go @@ -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() diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/error.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/error.go new file mode 100644 index 000000000..39ea75132 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/error.go @@ -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 +} + +// FXIME 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/expression.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/expression.go new file mode 100644 index 000000000..dc397b5cb --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/expression.go @@ -0,0 +1,815 @@ +package parser + +import ( + "regexp" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseIdentifier() *ast.Identifier { + literal := self.literal + idx := self.idx + self.next() + return &ast.Identifier{ + Name: literal, + Idx: idx, + } +} + +func (self *_parser) parsePrimaryExpression() ast.Expression { + literal := self.literal + idx := self.idx + switch self.token { + case token.IDENTIFIER: + self.next() + if len(literal) > 1 { + tkn, strict := token.IsKeyword(literal) + if tkn == token.KEYWORD { + if !strict { + self.error(idx, "Unexpected reserved word") + } + } + } + return &ast.Identifier{ + Name: literal, + Idx: idx, + } + case token.NULL: + self.next() + return &ast.NullLiteral{ + Idx: idx, + Literal: literal, + } + case token.BOOLEAN: + self.next() + value := false + switch literal { + case "true": + value = true + case "false": + value = false + default: + self.error(idx, "Illegal boolean literal") + } + return &ast.BooleanLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.STRING: + self.next() + value, err := parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + return &ast.StringLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.NUMBER: + self.next() + value, err := parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + value = 0 + } + return &ast.NumberLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.SLASH, token.QUOTIENT_ASSIGN: + return self.parseRegExpLiteral() + case token.LEFT_BRACE: + return self.parseObjectLiteral() + case token.LEFT_BRACKET: + return self.parseArrayLiteral() + case token.LEFT_PARENTHESIS: + self.expect(token.LEFT_PARENTHESIS) + expression := self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + return expression + case token.THIS: + self.next() + return &ast.ThisExpression{ + Idx: idx, + } + case token.FUNCTION: + return self.parseFunction(false) + } + + self.errorUnexpectedToken(self.token) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} +} + +func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { + + offset := self.chrOffset - 1 // Opening slash already gotten + if self.token == token.QUOTIENT_ASSIGN { + offset -= 1 // = + } + idx := self.idxOf(offset) + + pattern, err := self.scanString(offset) + endOffset := self.chrOffset + + self.next() + if err == nil { + pattern = pattern[1 : len(pattern)-1] + } + + flags := "" + if self.token == token.IDENTIFIER { // gim + + flags = self.literal + self.next() + endOffset = self.chrOffset - 1 + } + + var value string + // TODO 15.10 + { + // Test during parsing that this is a valid regular expression + // Sorry, (?=) and (?!) are invalid (for now) + pattern, err := TransformRegExp(pattern) + if err != nil { + if pattern == "" || self.mode&IgnoreRegExpErrors == 0 { + self.error(idx, "Invalid regular expression: %s", err.Error()) + } + } else { + _, err = regexp.Compile(pattern) + if err != nil { + // We should not get here, ParseRegExp should catch any errors + self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error" + } else { + value = pattern + } + } + } + + literal := self.str[offset:endOffset] + + return &ast.RegExpLiteral{ + Idx: idx, + Literal: literal, + Pattern: pattern, + Flags: flags, + Value: value, + } +} + +func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression { + + if self.token != token.IDENTIFIER { + idx := self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + + literal := self.literal + idx := self.idx + self.next() + node := &ast.VariableExpression{ + Name: literal, + Idx: idx, + } + + if declarationList != nil { + *declarationList = append(*declarationList, node) + } + + if self.token == token.ASSIGN { + self.next() + node.Initializer = self.parseAssignmentExpression() + } + + return node +} + +func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression { + + var declarationList []*ast.VariableExpression // Avoid bad expressions + var list []ast.Expression + + for { + list = append(list, self.parseVariableDeclaration(&declarationList)) + if self.token != token.COMMA { + break + } + self.next() + } + + self.scope.declare(&ast.VariableDeclaration{ + Var: var_, + List: declarationList, + }) + + return list +} + +func (self *_parser) parseObjectPropertyKey() (string, string) { + idx, tkn, literal := self.idx, self.token, self.literal + value := "" + self.next() + switch tkn { + case token.IDENTIFIER: + value = literal + case token.NUMBER: + var err error + _, err = parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + } else { + value = literal + } + case token.STRING: + var err error + value, err = parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + default: + // null, false, class, etc. + if matchIdentifier.MatchString(literal) { + value = literal + } + } + return literal, value +} + +func (self *_parser) parseObjectProperty() ast.Property { + + literal, value := self.parseObjectPropertyKey() + if literal == "get" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "get", + Value: node, + } + } else if literal == "set" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "set", + Value: node, + } + } + + self.expect(token.COLON) + + return ast.Property{ + Key: value, + Kind: "value", + Value: self.parseAssignmentExpression(), + } +} + +func (self *_parser) parseObjectLiteral() ast.Expression { + var value []ast.Property + idx0 := self.expect(token.LEFT_BRACE) + for self.token != token.RIGHT_BRACE && self.token != token.EOF { + property := self.parseObjectProperty() + value = append(value, property) + if self.token == token.COMMA { + self.next() + continue + } + } + idx1 := self.expect(token.RIGHT_BRACE) + + return &ast.ObjectLiteral{ + LeftBrace: idx0, + RightBrace: idx1, + Value: value, + } +} + +func (self *_parser) parseArrayLiteral() ast.Expression { + + idx0 := self.expect(token.LEFT_BRACKET) + var value []ast.Expression + for self.token != token.RIGHT_BRACKET && self.token != token.EOF { + if self.token == token.COMMA { + self.next() + value = append(value, nil) + continue + } + value = append(value, self.parseAssignmentExpression()) + if self.token != token.RIGHT_BRACKET { + self.expect(token.COMMA) + } + } + idx1 := self.expect(token.RIGHT_BRACKET) + + return &ast.ArrayLiteral{ + LeftBracket: idx0, + RightBracket: idx1, + Value: value, + } +} + +func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { + idx0 = self.expect(token.LEFT_PARENTHESIS) + if self.token != token.RIGHT_PARENTHESIS { + for { + argumentList = append(argumentList, self.parseAssignmentExpression()) + if self.token != token.COMMA { + break + } + self.next() + } + } + idx1 = self.expect(token.RIGHT_PARENTHESIS) + return +} + +func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { + argumentList, idx0, idx1 := self.parseArgumentList() + return &ast.CallExpression{ + Callee: left, + LeftParenthesis: idx0, + ArgumentList: argumentList, + RightParenthesis: idx1, + } +} + +func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { + period := self.expect(token.PERIOD) + + literal := self.literal + idx := self.idx + + if !matchIdentifier.MatchString(literal) { + self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: period, To: self.idx} + } + + self.next() + + return &ast.DotExpression{ + Left: left, + Identifier: ast.Identifier{ + Idx: idx, + Name: literal, + }, + } +} + +func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression { + idx0 := self.expect(token.LEFT_BRACKET) + member := self.parseExpression() + idx1 := self.expect(token.RIGHT_BRACKET) + return &ast.BracketExpression{ + LeftBracket: idx0, + Left: left, + Member: member, + RightBracket: idx1, + } +} + +func (self *_parser) parseNewExpression() ast.Expression { + idx := self.expect(token.NEW) + callee := self.parseLeftHandSideExpression() + node := &ast.NewExpression{ + New: idx, + Callee: callee, + } + if self.token == token.LEFT_PARENTHESIS { + argumentList, idx0, idx1 := self.parseArgumentList() + node.ArgumentList = argumentList + node.LeftParenthesis = idx0 + node.RightParenthesis = idx1 + } + return node +} + +func (self *_parser) parseLeftHandSideExpression() ast.Expression { + + var left ast.Expression + if self.token == token.NEW { + left = self.parseNewExpression() + } else { + left = self.parsePrimaryExpression() + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACE { + left = self.parseBracketMember(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + var left ast.Expression + if self.token == token.NEW { + left = self.parseNewExpression() + } else { + left = self.parsePrimaryExpression() + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACKET { + left = self.parseBracketMember(left) + } else if self.token == token.LEFT_PARENTHESIS { + left = self.parseCallExpression(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parsePostfixExpression() ast.Expression { + operand := self.parseLeftHandSideExpressionAllowCall() + + switch self.token { + case token.INCREMENT, token.DECREMENT: + // Make sure there is no line terminator here + if self.implicitSemicolon { + break + } + tkn := self.token + idx := self.idx + self.next() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + Postfix: true, + } + } + + return operand +} + +func (self *_parser) parseUnaryExpression() ast.Expression { + + switch self.token { + case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: + fallthrough + case token.DELETE, token.VOID, token.TYPEOF: + tkn := self.token + idx := self.idx + self.next() + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: self.parseUnaryExpression(), + } + case token.INCREMENT, token.DECREMENT: + tkn := self.token + idx := self.idx + self.next() + operand := self.parseUnaryExpression() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + } + } + + return self.parsePostfixExpression() +} + +func (self *_parser) parseMultiplicativeExpression() ast.Expression { + next := self.parseUnaryExpression + left := next() + + for self.token == token.MULTIPLY || self.token == token.SLASH || + self.token == token.REMAINDER { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseAdditiveExpression() ast.Expression { + next := self.parseMultiplicativeExpression + left := next() + + for self.token == token.PLUS || self.token == token.MINUS { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseShiftExpression() ast.Expression { + next := self.parseAdditiveExpression + left := next() + + for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT || + self.token == token.UNSIGNED_SHIFT_RIGHT { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseRelationalExpression() ast.Expression { + next := self.parseShiftExpression + left := next() + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + switch self.token { + case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: + tkn := self.token + self.next() + return &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + Comparison: true, + } + case token.INSTANCEOF: + tkn := self.token + self.next() + return &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + case token.IN: + if !allowIn { + return left + } + tkn := self.token + self.next() + return &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + } + + return left +} + +func (self *_parser) parseEqualityExpression() ast.Expression { + next := self.parseRelationalExpression + left := next() + + for self.token == token.EQUAL || self.token == token.NOT_EQUAL || + self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + Comparison: true, + } + } + + return left +} + +func (self *_parser) parseBitwiseAndExpression() ast.Expression { + next := self.parseEqualityExpression + left := next() + + for self.token == token.AND { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { + next := self.parseBitwiseAndExpression + left := next() + + for self.token == token.EXCLUSIVE_OR { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseOrExpression() ast.Expression { + next := self.parseBitwiseExclusiveOrExpression + left := next() + + for self.token == token.OR { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalAndExpression() ast.Expression { + next := self.parseBitwiseOrExpression + left := next() + + for self.token == token.LOGICAL_AND { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalOrExpression() ast.Expression { + next := self.parseLogicalAndExpression + left := next() + + for self.token == token.LOGICAL_OR { + tkn := self.token + self.next() + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseConditionlExpression() ast.Expression { + left := self.parseLogicalOrExpression() + + if self.token == token.QUESTION_MARK { + self.next() + consequent := self.parseAssignmentExpression() + self.expect(token.COLON) + return &ast.ConditionalExpression{ + Test: left, + Consequent: consequent, + Alternate: self.parseAssignmentExpression(), + } + } + + return left +} + +func (self *_parser) parseAssignmentExpression() ast.Expression { + left := self.parseConditionlExpression() + var operator token.Token + switch self.token { + case token.ASSIGN: + operator = self.token + case token.ADD_ASSIGN: + operator = token.PLUS + case token.SUBTRACT_ASSIGN: + operator = token.MINUS + case token.MULTIPLY_ASSIGN: + operator = token.MULTIPLY + case token.QUOTIENT_ASSIGN: + operator = token.SLASH + case token.REMAINDER_ASSIGN: + operator = token.REMAINDER + case token.AND_ASSIGN: + operator = token.AND + case token.AND_NOT_ASSIGN: + operator = token.AND_NOT + case token.OR_ASSIGN: + operator = token.OR + case token.EXCLUSIVE_OR_ASSIGN: + operator = token.EXCLUSIVE_OR + case token.SHIFT_LEFT_ASSIGN: + operator = token.SHIFT_LEFT + case token.SHIFT_RIGHT_ASSIGN: + operator = token.SHIFT_RIGHT + case token.UNSIGNED_SHIFT_RIGHT_ASSIGN: + operator = token.UNSIGNED_SHIFT_RIGHT + } + + if operator != 0 { + idx := self.idx + self.next() + switch left.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(left.Idx0(), "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + return &ast.AssignExpression{ + Left: left, + Operator: operator, + Right: self.parseAssignmentExpression(), + } + } + + return left +} + +func (self *_parser) parseExpression() ast.Expression { + next := self.parseAssignmentExpression + left := next() + + if self.token == token.COMMA { + sequence := []ast.Expression{left} + for { + if self.token != token.COMMA { + break + } + self.next() + sequence = append(sequence, next()) + } + return &ast.SequenceExpression{ + Sequence: sequence, + } + } + + return left +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer.go new file mode 100644 index 000000000..bc3e74f77 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer.go @@ -0,0 +1,819 @@ +package parser + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "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 + 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 == '/' { + self.skipSingleLineComment() + continue + } else if self.chr == '*' { + 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) 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.read() + } + fallthrough + case '\u2028', '\u2029', '\n': + if self.insertSemicolon { + return + } + 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:] // \ + + } else { + str = str[2:] // \ + 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] +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer_test.go new file mode 100644 index 000000000..37eb7a464 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/lexer_test.go @@ -0,0 +1,380 @@ +package parser + +import ( + "../terst" + "testing" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +var tt = terst.Terst +var is = terst.Is + +func TestLexer(t *testing.T) { + tt(t, func() { + setup := func(src string) *_parser { + parser := newParser("", src) + return parser + } + + test := func(src string, test ...interface{}) { + parser := setup(src) + for len(test) > 0 { + tkn, literal, idx := parser.scan() + if len(test) > 0 { + is(tkn, test[0].(token.Token)) + test = test[1:] + } + if len(test) > 0 { + is(literal, test[0].(string)) + test = test[1:] + } + if len(test) > 0 { + // FIXME terst, Fix this so that cast to file.Idx is not necessary? + is(idx, file.Idx(test[0].(int))) + test = test[1:] + } + } + } + + test("", + token.EOF, "", 1, + ) + + test("1", + token.NUMBER, "1", 1, + token.EOF, "", 2, + ) + + test(".0", + token.NUMBER, ".0", 1, + token.EOF, "", 3, + ) + + test("abc", + token.IDENTIFIER, "abc", 1, + token.EOF, "", 4, + ) + + test("abc(1)", + token.IDENTIFIER, "abc", 1, + token.LEFT_PARENTHESIS, "", 4, + token.NUMBER, "1", 5, + token.RIGHT_PARENTHESIS, "", 6, + token.EOF, "", 7, + ) + + test(".", + token.PERIOD, "", 1, + token.EOF, "", 2, + ) + + test("===.", + token.STRICT_EQUAL, "", 1, + token.PERIOD, "", 4, + token.EOF, "", 5, + ) + + test(">>>=.0", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + token.NUMBER, ".0", 5, + token.EOF, "", 7, + ) + + test(">>>=0.0.", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + token.NUMBER, "0.0", 5, + token.PERIOD, "", 8, + token.EOF, "", 9, + ) + + test("\"abc\"", + token.STRING, "\"abc\"", 1, + token.EOF, "", 6, + ) + + test("abc = //", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.EOF, "", 9, + ) + + test("abc = 1 / 2", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NUMBER, "1", 7, + token.SLASH, "", 9, + token.NUMBER, "2", 11, + token.EOF, "", 12, + ) + + test("xyzzy = 'Nothing happens.'", + token.IDENTIFIER, "xyzzy", 1, + token.ASSIGN, "", 7, + token.STRING, "'Nothing happens.'", 9, + token.EOF, "", 27, + ) + + test("abc = !false", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NOT, "", 7, + token.BOOLEAN, "false", 8, + token.EOF, "", 13, + ) + + test("abc = !!true", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NOT, "", 7, + token.NOT, "", 8, + token.BOOLEAN, "true", 9, + token.EOF, "", 13, + ) + + test("abc *= 1", + token.IDENTIFIER, "abc", 1, + token.MULTIPLY_ASSIGN, "", 5, + token.NUMBER, "1", 8, + token.EOF, "", 9, + ) + + test("if 1 else", + token.IF, "if", 1, + token.NUMBER, "1", 4, + token.ELSE, "else", 6, + token.EOF, "", 10, + ) + + test("null", + token.NULL, "null", 1, + token.EOF, "", 5, + ) + + test(`"\u007a\x79\u000a\x78"`, + token.STRING, "\"\\u007a\\x79\\u000a\\x78\"", 1, + token.EOF, "", 23, + ) + + test(`"[First line \ +Second line \ + Third line\ +. ]" + `, + token.STRING, "\"[First line \\\nSecond line \\\n Third line\\\n. ]\"", 1, + token.EOF, "", 53, + ) + + test("/", + token.SLASH, "", 1, + token.EOF, "", 2, + ) + + test("var abc = \"abc\uFFFFabc\"", + token.VAR, "var", 1, + token.IDENTIFIER, "abc", 5, + token.ASSIGN, "", 9, + token.STRING, "\"abc\uFFFFabc\"", 11, + token.EOF, "", 22, + ) + + test(`'\t' === '\r'`, + token.STRING, "'\\t'", 1, + token.STRICT_EQUAL, "", 6, + token.STRING, "'\\r'", 10, + token.EOF, "", 14, + ) + + test(`var \u0024 = 1`, + token.VAR, "var", 1, + token.IDENTIFIER, "$", 5, + token.ASSIGN, "", 12, + token.NUMBER, "1", 14, + token.EOF, "", 15, + ) + + test("10e10000", + token.NUMBER, "10e10000", 1, + token.EOF, "", 9, + ) + + test(`var if var class`, + token.VAR, "var", 1, + token.IF, "if", 5, + token.VAR, "var", 8, + token.KEYWORD, "class", 12, + token.EOF, "", 17, + ) + + test(`-0`, + token.MINUS, "", 1, + token.NUMBER, "0", 2, + token.EOF, "", 3, + ) + + test(`.01`, + token.NUMBER, ".01", 1, + token.EOF, "", 4, + ) + + test(`.01e+2`, + token.NUMBER, ".01e+2", 1, + token.EOF, "", 7, + ) + + test(";", + token.SEMICOLON, "", 1, + token.EOF, "", 2, + ) + + test(";;", + token.SEMICOLON, "", 1, + token.SEMICOLON, "", 2, + token.EOF, "", 3, + ) + + test("//", + token.EOF, "", 3, + ) + + test(";;//", + token.SEMICOLON, "", 1, + token.SEMICOLON, "", 2, + token.EOF, "", 5, + ) + + test("1", + token.NUMBER, "1", 1, + ) + + test("12 123", + token.NUMBER, "12", 1, + token.NUMBER, "123", 4, + ) + + test("1.2 12.3", + token.NUMBER, "1.2", 1, + token.NUMBER, "12.3", 5, + ) + + test("/ /=", + token.SLASH, "", 1, + token.QUOTIENT_ASSIGN, "", 3, + ) + + test(`"abc"`, + token.STRING, `"abc"`, 1, + ) + + test(`'abc'`, + token.STRING, `'abc'`, 1, + ) + + test("++", + token.INCREMENT, "", 1, + ) + + test(">", + token.GREATER, "", 1, + ) + + test(">=", + token.GREATER_OR_EQUAL, "", 1, + ) + + test(">>", + token.SHIFT_RIGHT, "", 1, + ) + + test(">>=", + token.SHIFT_RIGHT_ASSIGN, "", 1, + ) + + test(">>>", + token.UNSIGNED_SHIFT_RIGHT, "", 1, + ) + + test(">>>=", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + ) + + test("1 \"abc\"", + token.NUMBER, "1", 1, + token.STRING, "\"abc\"", 3, + ) + + test(",", + token.COMMA, "", 1, + ) + + test("1, \"abc\"", + token.NUMBER, "1", 1, + token.COMMA, "", 2, + token.STRING, "\"abc\"", 4, + ) + + test("new abc(1, 3.14159);", + token.NEW, "new", 1, + token.IDENTIFIER, "abc", 5, + token.LEFT_PARENTHESIS, "", 8, + token.NUMBER, "1", 9, + token.COMMA, "", 10, + token.NUMBER, "3.14159", 12, + token.RIGHT_PARENTHESIS, "", 19, + token.SEMICOLON, "", 20, + ) + + test("1 == \"1\"", + token.NUMBER, "1", 1, + token.EQUAL, "", 3, + token.STRING, "\"1\"", 6, + ) + + test("1\n[]\n", + token.NUMBER, "1", 1, + token.LEFT_BRACKET, "", 3, + token.RIGHT_BRACKET, "", 4, + ) + + test("1\ufeff[]\ufeff", + token.NUMBER, "1", 1, + token.LEFT_BRACKET, "", 5, + token.RIGHT_BRACKET, "", 6, + ) + + // ILLEGAL + + test(`3ea`, + token.ILLEGAL, "3e", 1, + token.IDENTIFIER, "a", 3, + token.EOF, "", 4, + ) + + test(`3in`, + token.ILLEGAL, "3", 1, + token.IN, "in", 2, + token.EOF, "", 4, + ) + + test("\"Hello\nWorld\"", + token.ILLEGAL, "", 1, + token.IDENTIFIER, "World", 8, + token.ILLEGAL, "", 13, + token.EOF, "", 14, + ) + + test("\u203f = 10", + token.ILLEGAL, "", 1, + token.ASSIGN, "", 5, + token.NUMBER, "10", 7, + token.EOF, "", 9, + ) + + test(`"\x0G"`, + token.STRING, "\"\\x0G\"", 1, + token.EOF, "", 7, + ) + + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/marshal_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/marshal_test.go new file mode 100644 index 000000000..f54cd2d4f --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/marshal_test.go @@ -0,0 +1,930 @@ +package parser + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + "testing" + + "github.com/robertkrimen/otto/ast" +) + +func marshal(name string, children ...interface{}) interface{} { + if len(children) == 1 { + if name == "" { + return testMarshalNode(children[0]) + } + return map[string]interface{}{ + name: children[0], + } + } + map_ := map[string]interface{}{} + length := len(children) / 2 + for i := 0; i < length; i++ { + name := children[i*2].(string) + value := children[i*2+1] + map_[name] = value + } + if name == "" { + return map_ + } + return map[string]interface{}{ + name: map_, + } +} + +func testMarshalNode(node interface{}) interface{} { + switch node := node.(type) { + + // Expression + + case *ast.ArrayLiteral: + return marshal("Array", testMarshalNode(node.Value)) + + case *ast.AssignExpression: + return marshal("Assign", + "Left", testMarshalNode(node.Left), + "Right", testMarshalNode(node.Right), + ) + + case *ast.BinaryExpression: + return marshal("BinaryExpression", + "Operator", node.Operator.String(), + "Left", testMarshalNode(node.Left), + "Right", testMarshalNode(node.Right), + ) + + case *ast.BooleanLiteral: + return marshal("Literal", node.Value) + + case *ast.CallExpression: + return marshal("Call", + "Callee", testMarshalNode(node.Callee), + "ArgumentList", testMarshalNode(node.ArgumentList), + ) + + case *ast.ConditionalExpression: + return marshal("Conditional", + "Test", testMarshalNode(node.Test), + "Consequent", testMarshalNode(node.Consequent), + "Alternate", testMarshalNode(node.Alternate), + ) + + case *ast.DotExpression: + return marshal("Dot", + "Left", testMarshalNode(node.Left), + "Member", node.Identifier.Name, + ) + + case *ast.NewExpression: + return marshal("New", + "Callee", testMarshalNode(node.Callee), + "ArgumentList", testMarshalNode(node.ArgumentList), + ) + + case *ast.NullLiteral: + return marshal("Literal", nil) + + case *ast.NumberLiteral: + return marshal("Literal", node.Value) + + case *ast.ObjectLiteral: + return marshal("Object", testMarshalNode(node.Value)) + + case *ast.RegExpLiteral: + return marshal("Literal", node.Literal) + + case *ast.StringLiteral: + return marshal("Literal", node.Literal) + + case *ast.VariableExpression: + return []interface{}{node.Name, testMarshalNode(node.Initializer)} + + // Statement + + case *ast.Program: + return testMarshalNode(node.Body) + + case *ast.BlockStatement: + return marshal("BlockStatement", testMarshalNode(node.List)) + + case *ast.EmptyStatement: + return "EmptyStatement" + + case *ast.ExpressionStatement: + return testMarshalNode(node.Expression) + + case *ast.ForInStatement: + return marshal("ForIn", + "Into", marshal("", node.Into), + "Source", marshal("", node.Source), + "Body", marshal("", node.Body), + ) + + case *ast.FunctionLiteral: + return marshal("Function", testMarshalNode(node.Body)) + + case *ast.Identifier: + return marshal("Identifier", node.Name) + + case *ast.IfStatement: + if_ := marshal("", + "Test", testMarshalNode(node.Test), + "Consequent", testMarshalNode(node.Consequent), + ).(map[string]interface{}) + if node.Alternate != nil { + if_["Alternate"] = testMarshalNode(node.Alternate) + } + return marshal("If", if_) + + case *ast.LabelledStatement: + return marshal("Label", + "Name", node.Label.Name, + "Statement", testMarshalNode(node.Statement), + ) + case ast.Property: + return marshal("", + "Key", node.Key, + "Value", testMarshalNode(node.Value), + ) + + case *ast.ReturnStatement: + return marshal("Return", testMarshalNode(node.Argument)) + + case *ast.SequenceExpression: + return marshal("Sequence", testMarshalNode(node.Sequence)) + + case *ast.ThrowStatement: + return marshal("Throw", testMarshalNode(node.Argument)) + + case *ast.VariableStatement: + return marshal("Var", testMarshalNode(node.List)) + + } + + { + value := reflect.ValueOf(node) + if value.Kind() == reflect.Slice { + tmp0 := []interface{}{} + for index := 0; index < value.Len(); index++ { + tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface())) + } + return tmp0 + } + } + + if node != nil { + fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node) + } + + return nil +} + +func testMarshal(node interface{}) string { + value, err := json.Marshal(testMarshalNode(node)) + if err != nil { + panic(err) + } + return string(value) +} + +func TestParserAST(t *testing.T) { + tt(t, func() { + + test := func(inputOutput string) { + match := matchBeforeAfterSeparator.FindStringIndex(inputOutput) + input := strings.TrimSpace(inputOutput[0:match[0]]) + wantOutput := strings.TrimSpace(inputOutput[match[1]:]) + _, program, err := testParse(input) + is(err, nil) + haveOutput := testMarshal(program) + tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{} + json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ") + json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ") + is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String()) + } + + test(` + --- +[] + `) + + test(` + ; + --- +[ + "EmptyStatement" +] + `) + + test(` + ;;; + --- +[ + "EmptyStatement", + "EmptyStatement", + "EmptyStatement" +] + `) + + test(` + 1; true; abc; "abc"; null; + --- +[ + { + "Literal": 1 + }, + { + "Literal": true + }, + { + "Identifier": "abc" + }, + { + "Literal": "\"abc\"" + }, + { + "Literal": null + } +] + `) + + test(` + { 1; null; 3.14159; ; } + --- +[ + { + "BlockStatement": [ + { + "Literal": 1 + }, + { + "Literal": null + }, + { + "Literal": 3.14159 + }, + "EmptyStatement" + ] + } +] + `) + + test(` + new abc(); + --- +[ + { + "New": { + "ArgumentList": [], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + new abc(1, 3.14159) + --- +[ + { + "New": { + "ArgumentList": [ + { + "Literal": 1 + }, + { + "Literal": 3.14159 + } + ], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + true ? false : true + --- +[ + { + "Conditional": { + "Alternate": { + "Literal": true + }, + "Consequent": { + "Literal": false + }, + "Test": { + "Literal": true + } + } + } +] + `) + + test(` + true || false + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": true + }, + "Operator": "||", + "Right": { + "Literal": false + } + } + } +] + `) + + test(` + 0 + { abc: true } + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 0 + }, + "Operator": "+", + "Right": { + "Object": [ + { + "Key": "abc", + "Value": { + "Literal": true + } + } + ] + } + } + } +] + `) + + test(` + 1 == "1" + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 1 + }, + "Operator": "==", + "Right": { + "Literal": "\"1\"" + } + } + } +] + `) + + test(` + abc(1) + --- +[ + { + "Call": { + "ArgumentList": [ + { + "Literal": 1 + } + ], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + Math.pow(3, 2) + --- +[ + { + "Call": { + "ArgumentList": [ + { + "Literal": 3 + }, + { + "Literal": 2 + } + ], + "Callee": { + "Dot": { + "Left": { + "Identifier": "Math" + }, + "Member": "pow" + } + } + } + } +] + `) + + test(` + 1, 2, 3 + --- +[ + { + "Sequence": [ + { + "Literal": 1 + }, + { + "Literal": 2 + }, + { + "Literal": 3 + } + ] + } +] + `) + + test(` + / abc / gim; + --- +[ + { + "Literal": "/ abc / gim" + } +] + `) + + test(` + if (0) + 1; + --- +[ + { + "If": { + "Consequent": { + "Literal": 1 + }, + "Test": { + "Literal": 0 + } + } + } +] + `) + + test(` + 0+function(){ + return; + } + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 0 + }, + "Operator": "+", + "Right": { + "Function": { + "BlockStatement": [ + { + "Return": null + } + ] + } + } + } + } +] + `) + + test(` + xyzzy // Ignore it + // Ignore this + // And this + /* And all.. + + + + ... of this! + */ + "Nothing happens." + // And finally this + --- +[ + { + "Identifier": "xyzzy" + }, + { + "Literal": "\"Nothing happens.\"" + } +] + `) + + test(` + ((x & (x = 1)) !== 0) + --- +[ + { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "Identifier": "x" + }, + "Operator": "\u0026", + "Right": { + "Assign": { + "Left": { + "Identifier": "x" + }, + "Right": { + "Literal": 1 + } + } + } + } + }, + "Operator": "!==", + "Right": { + "Literal": 0 + } + } + } +] + `) + + test(` + { abc: 'def' } + --- +[ + { + "BlockStatement": [ + { + "Label": { + "Name": "abc", + "Statement": { + "Literal": "'def'" + } + } + } + ] + } +] + `) + + test(` + // This is not an object, this is a string literal with a label! + ({ abc: 'def' }) + --- +[ + { + "Object": [ + { + "Key": "abc", + "Value": { + "Literal": "'def'" + } + } + ] + } +] + `) + + test(` + [,] + --- +[ + { + "Array": [ + null + ] + } +] + `) + + test(` + [,,] + --- +[ + { + "Array": [ + null, + null + ] + } +] + `) + + test(` + ({ get abc() {} }) + --- +[ + { + "Object": [ + { + "Key": "abc", + "Value": { + "Function": { + "BlockStatement": [] + } + } + } + ] + } +] + `) + + test(` + /abc/.source + --- +[ + { + "Dot": { + "Left": { + "Literal": "/abc/" + }, + "Member": "source" + } + } +] + `) + + test(` + xyzzy + + throw new TypeError("Nothing happens.") + --- +[ + { + "Identifier": "xyzzy" + }, + { + "Throw": { + "New": { + "ArgumentList": [ + { + "Literal": "\"Nothing happens.\"" + } + ], + "Callee": { + "Identifier": "TypeError" + } + } + } + } +] + `) + + // When run, this will call a type error to be thrown + // This is essentially the same as: + // + // var abc = 1(function(){})() + // + test(` + var abc = 1 + (function(){ + })() + --- +[ + { + "Var": [ + [ + "abc", + { + "Call": { + "ArgumentList": [], + "Callee": { + "Call": { + "ArgumentList": [ + { + "Function": { + "BlockStatement": [] + } + } + ], + "Callee": { + "Literal": 1 + } + } + } + } + } + ] + ] + } +] + `) + + test(` + "use strict" + --- +[ + { + "Literal": "\"use strict\"" + } +] + `) + + test(` + "use strict" + abc = 1 + 2 + 11 + --- +[ + { + "Literal": "\"use strict\"" + }, + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "Literal": 1 + }, + "Operator": "+", + "Right": { + "Literal": 2 + } + } + }, + "Operator": "+", + "Right": { + "Literal": 11 + } + } + } + } + } +] + `) + + test(` + abc = function() { 'use strict' } + --- +[ + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "Function": { + "BlockStatement": [ + { + "Literal": "'use strict'" + } + ] + } + } + } + } +] + `) + + test(` + for (var abc in def) { + } + --- +[ + { + "ForIn": { + "Body": { + "BlockStatement": [] + }, + "Into": [ + "abc", + null + ], + "Source": { + "Identifier": "def" + } + } + } +] + `) + + test(` + abc = { + '"': "'", + "'": '"', + } + --- +[ + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "Object": [ + { + "Key": "\"", + "Value": { + "Literal": "\"'\"" + } + }, + { + "Key": "'", + "Value": { + "Literal": "'\"'" + } + } + ] + } + } + } +] + `) + + return + + test(` + if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) { + } + --- +[ + { + "If": { + "Consequent": { + "BlockStatement": [] + }, + "Test": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": null, + "Operator": "\u0026\u0026", + "Right": { + "Call": { + "ArgumentList": [ + { + "Identifier": "def" + } + ], + "Callee": { + "Dot": { + "Left": { + "Identifier": "abc" + }, + "Member": "jkl" + } + } + } + } + } + }, + "Operator": "\u0026\u0026", + "Right": { + "BinaryExpression": { + "Left": null, + "Operator": "===", + "Right": null + } + } + } + }, + "Operator": "\u0026\u0026", + "Right": { + "BinaryExpression": { + "Left": { + "Dot": { + "Left": { + "Identifier": "abc" + }, + "Member": "length" + } + }, + "Operator": "\u003c", + "Right": { + "Identifier": "ghi" + } + } + } + } + } + } + } +] + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser.go new file mode 100644 index 000000000..37146aee9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser.go @@ -0,0 +1,270 @@ +/* +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" + "errors" + "io" + "io/ioutil" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +// 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) +) + +type _parser struct { + filename string + 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 +} + +func _newParser(filename, src string, base int) *_parser { + return &_parser{ + chr: ' ', // This is set so we can start scanning by skipping whitespace + str: src, + length: len(src), + base: base, + } +} + +func newParser(filename, src string) *_parser { + return _newParser(filename, src, 1) +} + +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) +} + +// 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) { + str, err := ReadSource(filename, src) + if err != nil { + return nil, err + } + { + str := string(str) + + base := 1 + if fileSet != nil { + base = fileSet.AddFile(filename, str) + } + + parser := _newParser(filename, str, base) + parser.mode = mode + return parser.parse() + } +} + +// 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) + program, err := parser.parse() + if err != nil { + return nil, err + } + + return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil +} + +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() + } + 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.filename + line, last := lineCount(str) + position.Line = 1 + line + if last >= 0 { + position.Column = offset - last + } else { + position.Column = 1 + len(str) + } + + return position +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser_test.go new file mode 100644 index 000000000..8f9457745 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/parser_test.go @@ -0,0 +1,1004 @@ +package parser + +import ( + "errors" + "regexp" + "strings" + "testing" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" +) + +func firstErr(err error) error { + switch err := err.(type) { + case ErrorList: + return err[0] + } + return err +} + +var matchBeforeAfterSeparator = regexp.MustCompile(`(?m)^[ \t]*---$`) + +func testParse(src string) (parser *_parser, program *ast.Program, err error) { + defer func() { + if tmp := recover(); tmp != nil { + switch tmp := tmp.(type) { + case string: + if strings.HasPrefix(tmp, "SyntaxError:") { + parser = nil + program = nil + err = errors.New(tmp) + return + } + } + panic(tmp) + } + }() + parser = newParser("", src) + program, err = parser.parse() + return +} + +func TestParseFile(t *testing.T) { + tt(t, func() { + _, err := ParseFile(nil, "", `/abc/`, 0) + is(err, nil) + + _, err = ParseFile(nil, "", `/(?!def)abc/`, IgnoreRegExpErrors) + is(err, nil) + + _, err = ParseFile(nil, "", `/(?!def)abc/`, 0) + is(err, "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") + + _, err = ParseFile(nil, "", `/(?!def)abc/; return`, IgnoreRegExpErrors) + is(err, "(anonymous): Line 1:15 Illegal return statement") + }) +} + +func TestParseFunction(t *testing.T) { + tt(t, func() { + test := func(prm, bdy string, expect interface{}) *ast.FunctionLiteral { + function, err := ParseFunction(prm, bdy) + is(firstErr(err), expect) + return function + } + + test("a, b,c,d", "", nil) + + test("a, b;,c,d", "", "(anonymous): Line 1:15 Unexpected token ;") + + test("this", "", "(anonymous): Line 1:11 Unexpected token this") + + test("a, b, c, null", "", "(anonymous): Line 1:20 Unexpected token null") + + test("a, b,c,d", "return;", nil) + + test("a, b,c,d", "break;", "(anonymous): Line 2:1 Illegal break statement") + + test("a, b,c,d", "{}", nil) + }) +} + +func TestParserErr(t *testing.T) { + tt(t, func() { + test := func(input string, expect interface{}) (*ast.Program, *_parser) { + parser := newParser("", input) + program, err := parser.parse() + is(firstErr(err), expect) + return program, parser + } + + program, parser := test("", nil) + + program, parser = test(` + var abc; + break; do { + } while(true); + `, "(anonymous): Line 3:9 Illegal break statement") + { + stmt := program.Body[1].(*ast.BadStatement) + is(parser.position(stmt.From).Column, 9) + is(parser.position(stmt.To).Column, 16) + is(parser.slice(stmt.From, stmt.To), "break; ") + } + + test("{", "(anonymous): Line 1:2 Unexpected end of input") + + test("}", "(anonymous): Line 1:1 Unexpected token }") + + test("3ea", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3in", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3in []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e+", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e-", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3x0", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("0x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("09", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("018", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("01.0", "(anonymous): Line 1:3 Unexpected number") + + test("01a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("0x3in[]", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\"Hello\nWorld\"", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\u203f = 10", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("/\n", "(anonymous): Line 1:1 Invalid regular expression: missing /") + + test("var x = /(s/g", "(anonymous): Line 1:9 Invalid regular expression: Unterminated group") + + test("0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("func() = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("(1 + 1) = 2", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("1++", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("1--", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("--1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("for((1 + 1) in abc) def();", "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test("[", "(anonymous): Line 1:2 Unexpected end of input") + + test("[,", "(anonymous): Line 1:3 Unexpected end of input") + + test("1 + {", "(anonymous): Line 1:6 Unexpected end of input") + + test("1 + { abc:abc", "(anonymous): Line 1:14 Unexpected end of input") + + test("1 + { abc:abc,", "(anonymous): Line 1:15 Unexpected end of input") + + test("var abc = /\n/", "(anonymous): Line 1:11 Invalid regular expression: missing /") + + test("var abc = \"\n", "(anonymous): Line 1:11 Unexpected token ILLEGAL") + + test("var if = 0", "(anonymous): Line 1:5 Unexpected token if") + + test("abc + 0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("+abc = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("1 + (", "(anonymous): Line 1:6 Unexpected end of input") + + test("\n\n\n{", "(anonymous): Line 4:2 Unexpected end of input") + + test("\n/* Some multiline\ncomment */\n)", "(anonymous): Line 4:1 Unexpected token )") + + // TODO + //{ set 1 } + //{ get 2 } + //({ set: s(if) { } }) + //({ set s(.) { } }) + //({ set: s() { } }) + //({ set: s(a, b) { } }) + //({ get: g(d) { } }) + //({ get i() { }, i: 42 }) + //({ i: 42, get i() { } }) + //({ set i(x) { }, i: 42 }) + //({ i: 42, set i(x) { } }) + //({ get i() { }, get i() { } }) + //({ set i(x) { }, set i(x) { } }) + + test("function abc(if) {}", "(anonymous): Line 1:14 Unexpected token if") + + test("function abc(true) {}", "(anonymous): Line 1:14 Unexpected token true") + + test("function abc(false) {}", "(anonymous): Line 1:14 Unexpected token false") + + test("function abc(null) {}", "(anonymous): Line 1:14 Unexpected token null") + + test("function null() {}", "(anonymous): Line 1:10 Unexpected token null") + + test("function true() {}", "(anonymous): Line 1:10 Unexpected token true") + + test("function false() {}", "(anonymous): Line 1:10 Unexpected token false") + + test("function if() {}", "(anonymous): Line 1:10 Unexpected token if") + + test("a b;", "(anonymous): Line 1:3 Unexpected identifier") + + test("if.a", "(anonymous): Line 1:3 Unexpected token .") + + test("a if", "(anonymous): Line 1:3 Unexpected token if") + + test("a class", "(anonymous): Line 1:3 Unexpected reserved word") + + test("break\n", "(anonymous): Line 1:1 Illegal break statement") + + test("break 1;", "(anonymous): Line 1:7 Unexpected number") + + test("for (;;) { break 1; }", "(anonymous): Line 1:18 Unexpected number") + + test("continue\n", "(anonymous): Line 1:1 Illegal continue statement") + + test("continue 1;", "(anonymous): Line 1:10 Unexpected number") + + test("for (;;) { continue 1; }", "(anonymous): Line 1:21 Unexpected number") + + test("throw", "(anonymous): Line 1:1 Unexpected end of input") + + test("throw;", "(anonymous): Line 1:6 Unexpected token ;") + + test("throw \n", "(anonymous): Line 1:1 Unexpected end of input") + + test("for (var abc, def in {});", "(anonymous): Line 1:19 Unexpected token in") + + test("for ((abc in {});;);", nil) + + test("for ((abc in {}));", "(anonymous): Line 1:17 Unexpected token )") + + test("for (+abc in {});", "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test("if (false)", "(anonymous): Line 1:11 Unexpected end of input") + + test("if (false) abc(); else", "(anonymous): Line 1:23 Unexpected end of input") + + test("do", "(anonymous): Line 1:3 Unexpected end of input") + + test("while (false)", "(anonymous): Line 1:14 Unexpected end of input") + + test("for (;;)", "(anonymous): Line 1:9 Unexpected end of input") + + test("with (abc)", "(anonymous): Line 1:11 Unexpected end of input") + + test("try {}", "(anonymous): Line 1:1 Missing catch or finally after try") + + test("try {} catch {}", "(anonymous): Line 1:14 Unexpected token {") + + test("try {} catch () {}", "(anonymous): Line 1:15 Unexpected token )") + + test("\u203f = 1", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + // TODO + // const x = 12, y; + // const x, y = 12; + // const x; + // if(true) let a = 1; + // if(true) const a = 1; + + test(`new abc()."def"`, "(anonymous): Line 1:11 Unexpected string") + + test("/*", "(anonymous): Line 1:3 Unexpected end of input") + + test("/**", "(anonymous): Line 1:4 Unexpected end of input") + + test("/*\n\n\n", "(anonymous): Line 4:1 Unexpected end of input") + + test("/*\n\n\n*", "(anonymous): Line 4:2 Unexpected end of input") + + test("/*abc", "(anonymous): Line 1:6 Unexpected end of input") + + test("/*abc *", "(anonymous): Line 1:9 Unexpected end of input") + + test("\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("\r\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("\n\r]", "(anonymous): Line 3:1 Unexpected token ]") + + test("//\r\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("//\n\r]", "(anonymous): Line 3:1 Unexpected token ]") + + test("/abc\\\n/", "(anonymous): Line 1:1 Invalid regular expression: missing /") + + test("//\r \n]", "(anonymous): Line 3:1 Unexpected token ]") + + test("/*\r\n*/]", "(anonymous): Line 2:3 Unexpected token ]") + + test("/*\r \n*/]", "(anonymous): Line 3:3 Unexpected token ]") + + test("\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\abc", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u0000", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u200c = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u200D = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`"\`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`"\u`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("return", "(anonymous): Line 1:1 Illegal return statement") + + test("continue", "(anonymous): Line 1:1 Illegal continue statement") + + test("break", "(anonymous): Line 1:1 Illegal break statement") + + test("switch (abc) { default: continue; }", "(anonymous): Line 1:25 Illegal continue statement") + + test("do { abc } *", "(anonymous): Line 1:12 Unexpected token *") + + test("while (true) { break abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") + + test("while (true) { continue abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") + + test("abc: while (true) { (function(){ break abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") + + test("abc: while (true) { (function(){ abc: break abc; }); }", nil) + + test("abc: while (true) { (function(){ continue abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") + + test(`abc: if (0) break abc; else {}`, nil) + + test(`abc: if (0) { break abc; } else {}`, nil) + + test(`abc: if (0) { break abc } else {}`, nil) + + test("abc: while (true) { abc: while (true) {} }", "(anonymous): Line 1:21 Label 'abc' already exists") + + if false { + // TODO When strict mode is implemented + test("(function () { 'use strict'; delete abc; }())", "") + } + + test("_: _: while (true) {]", "(anonymous): Line 1:4 Label '_' already exists") + + test("_:\n_:\nwhile (true) {]", "(anonymous): Line 2:1 Label '_' already exists") + + test("_:\n _:\nwhile (true) {]", "(anonymous): Line 2:4 Label '_' already exists") + + test("/Xyzzy(?!Nothing happens)/", + "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") + + test("function(){}", "(anonymous): Line 1:9 Unexpected token (") + + test("\n/*/", "(anonymous): Line 2:4 Unexpected end of input") + + test("/*/.source", "(anonymous): Line 1:11 Unexpected end of input") + + test("/\\1/.source", "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid \\1 ") + + test("var class", "(anonymous): Line 1:5 Unexpected reserved word") + + test("var if", "(anonymous): Line 1:5 Unexpected token if") + + test("object Object", "(anonymous): Line 1:8 Unexpected identifier") + + test("[object Object]", "(anonymous): Line 1:9 Unexpected identifier") + + test("\\u0xyz", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`for (var abc, def in {}) {}`, "(anonymous): Line 1:19 Unexpected token in") + + test(`for (abc, def in {}) {}`, "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test(`for (var abc=def, ghi=("abc" in {}); true;) {}`, nil) + + { + // Semicolon insertion + + test("this\nif (1);", nil) + + test("while (1) { break\nif (1); }", nil) + + test("throw\nif (1);", "(anonymous): Line 1:1 Illegal newline after throw") + + test("(function(){ return\nif (1); })", nil) + + test("while (1) { continue\nif (1); }", nil) + + test("debugger\nif (1);", nil) + } + + { // Reserved words + + test("class", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.class = 1", nil) + test("var class;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("const", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.const = 1", nil) + test("var const;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("enum", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.enum = 1", nil) + test("var enum;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("export", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.export = 1", nil) + test("var export;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("extends", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.extends = 1", nil) + test("var extends;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("import", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.import = 1", nil) + test("var import;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("super", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.super = 1", nil) + test("var super;", "(anonymous): Line 1:5 Unexpected reserved word") + } + + { // Reserved words (strict) + + test(`implements`, nil) + test(`abc.implements = 1`, nil) + test(`var implements;`, nil) + + test(`interface`, nil) + test(`abc.interface = 1`, nil) + test(`var interface;`, nil) + + test(`let`, nil) + test(`abc.let = 1`, nil) + test(`var let;`, nil) + + test(`package`, nil) + test(`abc.package = 1`, nil) + test(`var package;`, nil) + + test(`private`, nil) + test(`abc.private = 1`, nil) + test(`var private;`, nil) + + test(`protected`, nil) + test(`abc.protected = 1`, nil) + test(`var protected;`, nil) + + test(`public`, nil) + test(`abc.public = 1`, nil) + test(`var public;`, nil) + + test(`static`, nil) + test(`abc.static = 1`, nil) + test(`var static;`, nil) + + test(`yield`, nil) + test(`abc.yield = 1`, nil) + test(`var yield;`, nil) + } + }) +} + +func TestParser(t *testing.T) { + tt(t, func() { + test := func(source string, chk interface{}) *ast.Program { + _, program, err := testParse(source) + is(firstErr(err), chk) + return program + } + + test(` + abc + -- + [] + `, "(anonymous): Line 3:13 Invalid left-hand side in assignment") + + test(` + abc-- + [] + `, nil) + + test("1\n[]\n", "(anonymous): Line 2:2 Unexpected token ]") + + test(` + function abc() { + } + abc() + `, nil) + + program := test("", nil) + + test("//", nil) + + test("/* */", nil) + + test("/** **/", nil) + + test("/*****/", nil) + + test("/*", "(anonymous): Line 1:3 Unexpected end of input") + + test("#", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("/**/#", "(anonymous): Line 1:5 Unexpected token ILLEGAL") + + test("new +", "(anonymous): Line 1:5 Unexpected token +") + + program = test(";", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) + + program = test(";;", nil) + is(len(program.Body), 2) + is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) + is(program.Body[1].(*ast.EmptyStatement).Semicolon, file.Idx(2)) + + program = test("1.2", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") + + program = test("/* */1.2", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") + + program = test("\n", nil) + is(len(program.Body), 0) + + test(` + if (0) { + abc = 0 + } + else abc = 0 + `, nil) + + test("if (0) abc = 0 else abc = 0", "(anonymous): Line 1:16 Unexpected token else") + + test(` + if (0) { + abc = 0 + } else abc = 0 + `, nil) + + test(` + if (0) { + abc = 1 + } else { + } + `, nil) + + test(` + do { + } while (true) + `, nil) + + test(` + try { + } finally { + } + `, nil) + + test(` + try { + } catch (abc) { + } finally { + } + `, nil) + + test(` + try { + } + catch (abc) { + } + finally { + } + `, nil) + + test(`try {} catch (abc) {} finally {}`, nil) + + test(` + do { + do { + } while (0) + } while (0) + `, nil) + + test(` + (function(){ + try { + if ( + 1 + ) { + return 1 + } + return 0 + } finally { + } + })() + `, nil) + + test("abc = ''\ndef", nil) + + test("abc = 1\ndef", nil) + + test("abc = Math\ndef", nil) + + test(`"\'"`, nil) + + test(` + abc = function(){ + } + abc = 0 + `, nil) + + test("abc.null = 0", nil) + + test("0x41", nil) + + test(`"\d"`, nil) + + test(`(function(){return this})`, nil) + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + abc = [101]; + abc.hasOwnProperty("0") && abc[0] === 101; + `, nil) + + test(`new abc()`, nil) + test(`new {}`, nil) + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + } + else { + break + } + result = result + 1 + } + `, nil) + + test(` + while (0) { + if (0) { + continue + } + } + `, nil) + + test("var \u0061\u0062\u0063 = 0", nil) + + // 7_3_1 + test("var test7_3_1\nabc = 66;", nil) + test("var test7_3_1\u2028abc = 66;", nil) + + // 7_3_3 + test("//\u2028 =;", "(anonymous): Line 2:2 Unexpected token =") + + // 7_3_10 + test("var abc = \u2029;", "(anonymous): Line 2:1 Unexpected token ;") + test("var abc = \\u2029;", "(anonymous): Line 1:11 Unexpected token ILLEGAL") + test("var \\u0061\\u0062\\u0063 = 0;", nil) + + test("'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("'\nstr\ning\n'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + // S7.6_A4.3_T1 + test(`var $\u0030 = 0;`, nil) + + // S7.6.1.1_A1.1 + test(`switch = 1`, "(anonymous): Line 1:8 Unexpected token =") + + // S7.8.3_A2.1_T1 + test(`.0 === 0.0`, nil) + + // 7.8.5-1 + test("var regExp = /\\\rn/;", "(anonymous): Line 1:14 Invalid regular expression: missing /") + + // S7.8.5_A1.1_T2 + test("var regExp = /=/;", nil) + + // S7.8.5_A1.2_T1 + test("/*/", "(anonymous): Line 1:4 Unexpected end of input") + + // Sbp_7.9_A9_T3 + test(` + do { + ; + } while (false) true + `, nil) + + // S7.9_A10_T10 + test(` + {a:1 + } 3 + `, nil) + + test(` + abc + ++def + `, nil) + + // S7.9_A5.2_T1 + test(` + for(false;false + ) { + break; + } + `, "(anonymous): Line 3:13 Unexpected token )") + + // S7.9_A9_T8 + test(` + do {}; + while (false) + `, "(anonymous): Line 2:18 Unexpected token ;") + + // S8.4_A5 + test(` + "x\0y" + `, nil) + + // S9.3.1_A6_T1 + test(` + 10e10000 + `, nil) + + // 10.4.2-1-5 + test(` + "abc\ + def" + `, nil) + + test("'\\\n'", nil) + + test("'\\\r\n'", nil) + + //// 11.13.1-1-1 + test("42 = 42;", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + // S11.13.2_A4.2_T1.3 + test(` + abc /= "1" + `, nil) + + // 12.1-1 + test(` + try{};catch(){} + `, "(anonymous): Line 2:13 Missing catch or finally after try") + + // 12.1-3 + test(` + try{};finally{} + `, "(anonymous): Line 2:13 Missing catch or finally after try") + + // S12.6.3_A11.1_T3 + test(` + while (true) { + break abc; + } + `, "(anonymous): Line 3:17 Undefined label 'abc'") + + // S15.3_A2_T1 + test(`var x / = 1;`, "(anonymous): Line 1:7 Unexpected token /") + + test(` + function abc() { + if (0) + return; + else { + } + } + `, nil) + + test("//\u2028 var =;", "(anonymous): Line 2:6 Unexpected token =") + + test(` + throw + {} + `, "(anonymous): Line 2:13 Illegal newline after throw") + + // S7.6.1.1_A1.11 + test(` + function = 1 + `, "(anonymous): Line 2:22 Unexpected token =") + + // S7.8.3_A1.2_T1 + test(`0e1`, nil) + + test("abc = 1; abc\n++", "(anonymous): Line 2:3 Unexpected end of input") + + // --- + + test("({ get abc() {} })", nil) + + test(`for (abc.def in {}) {}`, nil) + + test(`while (true) { break }`, nil) + + test(`while (true) { continue }`, nil) + + test(`abc=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,def=/^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/`, nil) + + test(`(function() { try {} catch (err) {} finally {} return })`, nil) + + test(`0xde0b6b3a7640080.toFixed(0)`, nil) + + test(`/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/`, nil) + + test(`/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/`, nil) + + test("var abc = 1;\ufeff", nil) + + test("\ufeff/* var abc = 1; */", nil) + + test(`if (-0x8000000000000000<=abc&&abc<=0x8000000000000000) {}`, nil) + + test(`(function(){debugger;return this;})`, nil) + + test(` + + `, nil) + + test(` + var abc = "" + debugger + `, nil) + + test(` + var abc = /\[\]$/ + debugger + `, nil) + + test(` + var abc = 1 / + 2 + debugger + `, nil) + }) +} + +func Test_parseStringLiteral(t *testing.T) { + tt(t, func() { + test := func(have, want string) { + have, err := parseStringLiteral(have) + is(err, nil) + is(have, want) + } + + test("", "") + + test("1(\\\\d+)", "1(\\d+)") + + test("\\u2029", "\u2029") + + test("abc\\uFFFFabc", "abc\uFFFFabc") + + test("[First line \\\nSecond line \\\n Third line\\\n. ]", + "[First line Second line Third line. ]") + + test("\\u007a\\x79\\u000a\\x78", "zy\nx") + + // S7.8.4_A4.2_T3 + test("\\a", "a") + test("\u0410", "\u0410") + + // S7.8.4_A5.1_T1 + test("\\0", "\u0000") + + // S8.4_A5 + test("\u0000", "\u0000") + + // 15.5.4.20 + test("'abc'\\\n'def'", "'abc''def'") + + // 15.5.4.20-4-1 + test("'abc'\\\r\n'def'", "'abc''def'") + + // Octal + test("\\0", "\000") + test("\\00", "\000") + test("\\000", "\000") + test("\\09", "\0009") + test("\\009", "\0009") + test("\\0009", "\0009") + test("\\1", "\001") + test("\\01", "\001") + test("\\001", "\001") + test("\\0011", "\0011") + test("\\1abc", "\001abc") + + test("\\\u4e16", "\u4e16") + + // err + test = func(have, want string) { + have, err := parseStringLiteral(have) + is(err.Error(), want) + is(have, "") + } + + test(`\u`, `invalid escape: \u: len("") != 4`) + test(`\u0`, `invalid escape: \u: len("0") != 4`) + test(`\u00`, `invalid escape: \u: len("00") != 4`) + test(`\u000`, `invalid escape: \u: len("000") != 4`) + + test(`\x`, `invalid escape: \x: len("") != 2`) + test(`\x0`, `invalid escape: \x: len("0") != 2`) + test(`\x0`, `invalid escape: \x: len("0") != 2`) + }) +} + +func Test_parseNumberLiteral(t *testing.T) { + tt(t, func() { + test := func(input string, expect interface{}) { + result, err := parseNumberLiteral(input) + is(err, nil) + is(result, expect) + } + + test("0", 0) + + test("0x8000000000000000", float64(9.223372036854776e+18)) + }) +} + +func TestPosition(t *testing.T) { + tt(t, func() { + parser := newParser("", "// Lorem ipsum") + + // Out of range, idx0 (error condition) + is(parser.slice(0, 1), "") + is(parser.slice(0, 10), "") + + // Out of range, idx1 (error condition) + is(parser.slice(1, 128), "") + + is(parser.str[0:0], "") + is(parser.slice(1, 1), "") + + is(parser.str[0:1], "/") + is(parser.slice(1, 2), "/") + + is(parser.str[0:14], "// Lorem ipsum") + is(parser.slice(1, 15), "// Lorem ipsum") + + parser = newParser("", "(function(){ return 0; })") + program, err := parser.parse() + is(err, nil) + + var node ast.Node + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) + is(node.Idx0(), file.Idx(2)) + is(node.Idx1(), file.Idx(25)) + is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") + is(parser.slice(node.Idx0(), node.Idx1()+1), "function(){ return 0; })") + is(parser.slice(node.Idx0(), node.Idx1()+2), "") + is(node.(*ast.FunctionLiteral).Source, "function(){ return 0; }") + + node = program + is(node.Idx0(), file.Idx(2)) + is(node.Idx1(), file.Idx(25)) + is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") + + parser = newParser("", "(function(){ return abc; })") + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) + is(node.(*ast.FunctionLiteral).Source, "function(){ return abc; }") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp.go new file mode 100644 index 000000000..f614dae74 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp.go @@ -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) ", 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 ", 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 ", 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp_test.go new file mode 100644 index 000000000..3222db1a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/regexp_test.go @@ -0,0 +1,149 @@ +package parser + +import ( + "regexp" + "testing" +) + +func TestRegExp(t *testing.T) { + tt(t, func() { + { + // err + test := func(input string, expect interface{}) { + _, err := TransformRegExp(input) + is(err, expect) + } + + test("[", "Unterminated character class") + + test("(", "Unterminated group") + + test("(?=)", "re2: Invalid (?=) ") + + test("(?=)", "re2: Invalid (?=) ") + + test("(?!)", "re2: Invalid (?!) ") + + // An error anyway + test("(?=", "re2: Invalid (?=) ") + + test("\\1", "re2: Invalid \\1 ") + + test("\\90", "re2: Invalid \\90 ") + + test("\\9123456789", "re2: Invalid \\9123456789 ") + + test("\\(?=)", "Unmatched ')'") + + test(")", "Unmatched ')'") + } + + { + // err + test := func(input, expect string, expectErr interface{}) { + output, err := TransformRegExp(input) + is(output, expect) + is(err, expectErr) + } + + test("(?!)", "(?!)", "re2: Invalid (?!) ") + + test(")", "", "Unmatched ')'") + + test("(?!))", "", "re2: Invalid (?!) ") + + test("\\0", "\\0", nil) + + test("\\1", "\\1", "re2: Invalid \\1 ") + + test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 ") + } + + { + // err + test := func(input string, expect string) { + result, err := TransformRegExp(input) + is(err, nil) + if is(result, expect) { + _, err := regexp.Compile(result) + if !is(err, nil) { + t.Log(result) + } + } + } + + test("", "") + + test("abc", "abc") + + test(`\abc`, `abc`) + + test(`\a\b\c`, `a\bc`) + + test(`\x`, `x`) + + test(`\c`, `c`) + + test(`\cA`, `\x01`) + + test(`\cz`, `\x1a`) + + test(`\ca`, `\x01`) + + test(`\cj`, `\x0a`) + + test(`\ck`, `\x0b`) + + test(`\+`, `\+`) + + test(`[\b]`, `[\x08]`) + + test(`\u0z01\x\undefined`, `u0z01xundefined`) + + test(`\\|'|\r|\n|\t|\u2028|\u2029`, `\\|'|\r|\n|\t|\x{2028}|\x{2029}`) + + test("]", "]") + + test("}", "}") + + test("%", "%") + + test("(%)", "(%)") + + test("(?:[%\\s])", "(?:[%\\s])") + + test("[[]", "[[]") + + test("\\101", "\\x41") + + test("\\51", "\\x29") + + test("\\051", "\\x29") + + test("\\175", "\\x7d") + + test("\\04", "\\x04") + + test(`<%([\s\S]+?)%>`, `<%([\s\S]+?)%>`) + + test(`(.)^`, "(.)^") + + test(`<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`, `<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`) + + test(`\$`, `\$`) + + test(`[G-b]`, `[G-b]`) + + test(`[G-b\0]`, `[G-b\0]`) + } + }) +} + +func TestTransformRegExp(t *testing.T) { + tt(t, func() { + pattern, err := TransformRegExp(`\s+abc\s+`) + is(err, nil) + is(pattern, `\s+abc\s+`) + is(regexp.MustCompile(pattern).MatchString("\t abc def"), true) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/scope.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/scope.go new file mode 100644 index 000000000..e1dbdda13 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/scope.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser/statement.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser/statement.go new file mode 100644 index 000000000..4c08b523c --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser/statement.go @@ -0,0 +1,662 @@ +package parser + +import ( + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseBlockStatement() *ast.BlockStatement { + node := &ast.BlockStatement{} + node.LeftBrace = self.expect(token.LEFT_BRACE) + node.List = self.parseStatementList() + node.RightBrace = self.expect(token.RIGHT_BRACE) + + 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 { + list = append(list, self.parseStatement()) + } + + 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} + } + + 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: + return self.parseDoWhileStatement() + 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: + self.parseFunction(true) + // FIXME + return &ast.EmptyStatement{} + case token.SWITCH: + return self.parseSwitchStatement() + case token.RETURN: + return self.parseReturnStatement() + case token.THROW: + return self.parseThrowStatement() + case token.TRY: + return self.parseTryStatement() + } + + expression := self.parseExpression() + + if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { + // LabelledStatement + colon := self.idx + self.next() // : + label := identifier.Name + for _, value := range self.scope.labels { + if label == value { + self.error(identifier.Idx0(), "Label '%s' already exists", label) + } + } + 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 + return &ast.LabelledStatement{ + Label: identifier, + Colon: colon, + Statement: statement, + } + } + + self.optionalSemicolon() + + return &ast.ExpressionStatement{ + Expression: expression, + } +} + +func (self *_parser) parseTryStatement() ast.Statement { + + node := &ast.TryStatement{ + Try: self.expect(token.TRY), + Body: self.parseBlockStatement(), + } + + if self.token == token.CATCH { + catch := self.idx + 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.token == token.FINALLY { + self.next() + node.Finally = self.parseBlockStatement() + } + + 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) + var list []*ast.Identifier + for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + } else { + list = append(list, self.parseIdentifier()) + } + if self.token != token.RIGHT_PARENTHESIS { + 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) 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) + } + 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, + } + + self.semicolon() + + return node +} + +func (self *_parser) parseReturnStatement() ast.Statement { + idx := self.expect(token.RETURN) + + 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() + } + + self.semicolon() + + return node +} + +func (self *_parser) parseThrowStatement() ast.Statement { + 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{ + Argument: self.parseExpression(), + } + + self.semicolon() + + return node +} + +func (self *_parser) parseSwitchStatement() ast.Statement { + self.expect(token.SWITCH) + self.expect(token.LEFT_PARENTHESIS) + node := &ast.SwitchStatement{ + Discriminant: self.parseExpression(), + Default: -1, + } + self.expect(token.RIGHT_PARENTHESIS) + + 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) + } + + return node +} + +func (self *_parser) parseWithStatement() ast.Statement { + self.expect(token.WITH) + self.expect(token.LEFT_PARENTHESIS) + node := &ast.WithStatement{ + Object: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + + node.Body = self.parseStatement() + + return node +} + +func (self *_parser) parseCaseStatement() *ast.CaseStatement { + + node := &ast.CaseStatement{ + Case: self.idx, + } + if self.token == token.DEFAULT { + self.next() + } else { + self.expect(token.CASE) + node.Test = self.parseExpression() + } + self.expect(token.COLON) + + for { + if self.token == token.EOF || + self.token == token.RIGHT_BRACE || + self.token == token.CASE || + self.token == token.DEFAULT { + break + } + node.Consequent = append(node.Consequent, self.parseStatement()) + + } + + 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 " in" + + source := self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + + return &ast.ForInStatement{ + Into: into, + Source: source, + Body: self.parseIterationStatement(), + } +} + +func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement { + + // Already have consumed " ;" + + var test, update ast.Expression + + if self.token != token.SEMICOLON { + test = self.parseExpression() + } + self.expect(token.SEMICOLON) + + if self.token != token.RIGHT_PARENTHESIS { + update = self.parseExpression() + } + self.expect(token.RIGHT_PARENTHESIS) + + return &ast.ForStatement{ + Initializer: initializer, + Test: test, + Update: update, + Body: self.parseIterationStatement(), + } +} + +func (self *_parser) parseForOrForInStatement() ast.Statement { + idx := self.expect(token.FOR) + 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 + self.next() + list := self.parseVariableDeclarationList(var_) + if len(list) == 1 && self.token == token.IN { + self.next() // in + forIn = true + left = []ast.Expression{list[0]} // There is only one declaration + } else { + left = list + } + } 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} + } + return self.parseForIn(left[0]) + } + + self.expect(token.SEMICOLON) + return self.parseFor(&ast.SequenceExpression{Sequence: left}) +} + +func (self *_parser) parseVariableStatement() *ast.VariableStatement { + + idx := self.expect(token.VAR) + + list := self.parseVariableDeclarationList(idx) + self.semicolon() + + return &ast.VariableStatement{ + Var: idx, + List: list, + } +} + +func (self *_parser) parseDoWhileStatement() ast.Statement { + inIteration := self.scope.inIteration + self.scope.inIteration = true + defer func() { + self.scope.inIteration = inIteration + }() + + self.expect(token.DO) + node := &ast.DoWhileStatement{} + if self.token == token.LEFT_BRACE { + node.Body = self.parseBlockStatement() + } else { + node.Body = self.parseStatement() + } + + self.expect(token.WHILE) + self.expect(token.LEFT_PARENTHESIS) + node.Test = self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + + return node +} + +func (self *_parser) parseWhileStatement() ast.Statement { + self.expect(token.WHILE) + self.expect(token.LEFT_PARENTHESIS) + node := &ast.WhileStatement{ + Test: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + node.Body = self.parseIterationStatement() + + return node +} + +func (self *_parser) parseIfStatement() ast.Statement { + self.expect(token.IF) + self.expect(token.LEFT_PARENTHESIS) + node := &ast.IfStatement{ + 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() + } + + return node +} + +func (self *_parser) parseSourceElement() ast.Statement { + return self.parseStatement() +} + +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, + } +} + +func (self *_parser) parseBreakStatement() ast.Statement { + 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 + } + return &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + } + } + + 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() + return &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + Label: identifier, + } + } + + 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() + } +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/parser_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/parser_test.go new file mode 100644 index 000000000..7db43d239 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/parser_test.go @@ -0,0 +1,42 @@ +package otto + +import ( + "testing" +) + +func TestPersistence(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + function abc() { return 1; } + abc.toString(); + `, "function abc() { return 1; }") + + test(` + function def() { return 3.14159; } + [ abc.toString(), def.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; }") + + test(` + eval("function ghi() { return 'ghi' }"); + [ abc.toString(), def.toString(), ghi.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") + + test(` + [ abc.toString(), def.toString(), ghi.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") + + test(`/* + + + + + + + + + + */`, UndefinedValue()) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/property.go b/Godeps/_workspace/src/github.com/obscuren/otto/property.go new file mode 100644 index 000000000..400753820 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/property.go @@ -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].callGet(toValue(this)) + } + } + return UndefinedValue() +} + +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(value Value) (descriptor _property) { + objectDescriptor := value._object() + if objectDescriptor == nil { + panic(newTypeError()) + } + + { + descriptor.mode = modeSetMask // Initially nothing is set + if objectDescriptor.hasProperty("enumerable") { + if objectDescriptor.get("enumerable").toBoolean() { + descriptor.enumerateOn() + } else { + descriptor.enumerateOff() + } + } + + if objectDescriptor.hasProperty("configurable") { + if objectDescriptor.get("configurable").toBoolean() { + descriptor.configureOn() + } else { + descriptor.configureOff() + } + } + + if objectDescriptor.hasProperty("writable") { + if objectDescriptor.get("writable").toBoolean() { + 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(newTypeError()) + } + 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(newTypeError()) + } + setter = value._object() + getterSetter = true + } else { + setter = &_nilGetSetObject + getterSetter = true + } + } + + if getterSetter { + if descriptor.writeSet() { + panic(newTypeError()) + } + descriptor.value = _propertyGetSet{getter, setter} + } + + if objectDescriptor.hasProperty("value") { + if getterSetter { + panic(newTypeError()) + } + 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 := UndefinedValue() + if getSet[0] != nil { + get = toValue_object(getSet[0]) + } + set := UndefinedValue() + 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/reflect_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/reflect_test.go new file mode 100644 index 000000000..f91b38c52 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/reflect_test.go @@ -0,0 +1,411 @@ +package otto + +import ( + "math" + "reflect" + "testing" +) + +type testStruct struct { + Abc bool + Def int + Ghi string + Jkl interface{} +} + +func (t *testStruct) FuncPointerReciever() string { + return "abc" +} + +func (t testStruct) FuncNoArgsNoRet() { + return +} + +func (t testStruct) FuncNoArgs() string { + return "abc" +} + +func (t testStruct) FuncNoArgsMultRet() (string, error) { + return "def", nil +} + +func (t testStruct) FuncOneArgs(a string) string { + return a +} + +func (t testStruct) FuncMultArgs(a, b string) string { + return a + b +} + +func (t testStruct) FuncVarArgs(as ...string) int { + return len(as) +} + +func TestReflect(t *testing.T) { + return + tt(t, func() { + // Testing dbgf + // These should panic + toValue("Xyzzy").toReflectValue(reflect.Ptr) + stringToReflectValue("Xyzzy", reflect.Ptr) + }) +} + +func Test_reflectStruct(t *testing.T) { + tt(t, func() { + test, vm := test() + + // testStruct + { + abc := &testStruct{} + vm.Set("abc", abc) + + test(` + abc.FuncPointerReciever(); + `, "abc") + + test(` + [ abc.Abc, abc.Ghi ]; + `, "false,") + + abc.Abc = true + abc.Ghi = "Nothing happens." + + test(` + [ abc.Abc, abc.Ghi ]; + `, "true,Nothing happens.") + + *abc = testStruct{} + + test(` + [ abc.Abc, abc.Ghi ]; + `, "false,") + + abc.Abc = true + abc.Ghi = "Xyzzy" + vm.Set("abc", abc) + + test(` + [ abc.Abc, abc.Ghi ]; + `, "true,Xyzzy") + + is(abc.Abc, true) + test(` + abc.Abc = false; + abc.Def = 451; + abc.Ghi = "Nothing happens."; + abc.abc = "Something happens."; + [ abc.Def, abc.abc ]; + `, "451,Something happens.") + is(abc.Abc, false) + is(abc.Def, 451) + is(abc.Ghi, "Nothing happens.") + + test(` + delete abc.Def; + delete abc.abc; + [ abc.Def, abc.abc ]; + `, "451,") + is(abc.Def, 451) + + test(` + abc.FuncNoArgsNoRet(); + `, "undefined") + test(` + abc.FuncNoArgs(); + `, "abc") + test(` + abc.FuncOneArgs("abc"); + `, "abc") + test(` + abc.FuncMultArgs("abc", "def"); + `, "abcdef") + test(` + abc.FuncVarArgs("abc", "def", "ghi"); + `, 3) + + test(`raise: + abc.FuncNoArgsMultRet(); + `, "TypeError") + } + }) +} + +func Test_reflectMap(t *testing.T) { + tt(t, func() { + test, vm := test() + + // map[string]string + { + abc := map[string]string{ + "Xyzzy": "Nothing happens.", + "def": "1", + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "Nothing happens.,1,") + + is(abc["xyz"], "pqr") + } + + // map[string]float64 + { + abc := map[string]float64{ + "Xyzzy": math.Pi, + "def": 1, + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + abc.jkl = 10; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "3.141592653589793,1,") + + is(abc["xyz"], math.NaN()) + is(abc["jkl"], float64(10)) + } + + // map[string]int32 + { + abc := map[string]int32{ + "Xyzzy": 3, + "def": 1, + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + abc.jkl = 10; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "3,1,") + + is(abc["xyz"], 0) + is(abc["jkl"], int32(10)) + + test(` + delete abc["Xyzzy"]; + `) + + _, exists := abc["Xyzzy"] + is(exists, false) + is(abc["Xyzzy"], 0) + } + + // map[int32]string + { + abc := map[int32]string{ + 0: "abc", + 1: "def", + } + vm.Set("abc", abc) + + test(` + abc[2] = "pqr"; + //abc.jkl = 10; + abc[3] = 10; + [ abc[0], abc[1], abc[2], abc[3] ] + `, "abc,def,pqr,10") + + is(abc[2], "pqr") + is(abc[3], "10") + + test(` + delete abc[2]; + `) + + _, exists := abc[2] + is(exists, false) + } + + }) +} + +func Test_reflectSlice(t *testing.T) { + tt(t, func() { + test, vm := test() + + // []bool + { + abc := []bool{ + false, + true, + true, + false, + } + vm.Set("abc", abc) + + test(` + abc; + `, "false,true,true,false") + + test(` + abc[0] = true; + abc[abc.length-1] = true; + delete abc[2]; + abc; + `, "true,true,false,true") + + is(abc, []bool{true, true, false, true}) + is(abc[len(abc)-1], true) + } + + // []int32 + { + abc := make([]int32, 4) + vm.Set("abc", abc) + + test(` + abc; + `, "0,0,0,0") + + test(` + abc[0] = 4.2; + abc[1] = "42"; + abc[2] = 3.14; + abc; + `, "4,42,3,0") + + is(abc, []int32{4, 42, 3, 0}) + + test(` + delete abc[1]; + delete abc[2]; + `) + is(abc[1], 0) + is(abc[2], 0) + } + }) +} + +func Test_reflectArray(t *testing.T) { + tt(t, func() { + test, vm := test() + + // []bool + { + abc := [4]bool{ + false, + true, + true, + false, + } + vm.Set("abc", abc) + + test(` + abc; + `, "false,true,true,false") + // Unaddressable array + + test(` + abc[0] = true; + abc[abc.length-1] = true; + abc; + `, "false,true,true,false") + // Again, unaddressable array + + is(abc, [4]bool{false, true, true, false}) + is(abc[len(abc)-1], false) + // ... + } + + // []int32 + { + abc := make([]int32, 4) + vm.Set("abc", abc) + + test(` + abc; + `, "0,0,0,0") + + test(` + abc[0] = 4.2; + abc[1] = "42"; + abc[2] = 3.14; + abc; + `, "4,42,3,0") + + is(abc, []int32{4, 42, 3, 0}) + } + + // []bool + { + abc := [4]bool{ + false, + true, + true, + false, + } + vm.Set("abc", &abc) + + test(` + abc; + `, "false,true,true,false") + + test(` + abc[0] = true; + abc[abc.length-1] = true; + delete abc[2]; + abc; + `, "true,true,false,true") + + is(abc, [4]bool{true, true, false, true}) + is(abc[len(abc)-1], true) + } + + }) +} + +func Test_reflectArray_concat(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("ghi", []string{"jkl", "mno"}) + vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) + test(` + var def = { + "abc": ["abc"], + "xyz": ["xyz"] + }; + xyz = pqr.concat(ghi, def.abc, def, def.xyz); + [ xyz, xyz.length ]; + `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz,9") + }) +} + +func Test_reflectMapInterface(t *testing.T) { + tt(t, func() { + test, vm := test() + + { + abc := map[string]interface{}{ + "Xyzzy": "Nothing happens.", + "def": "1", + "jkl": "jkl", + } + vm.Set("abc", abc) + vm.Set("mno", &testStruct{}) + + test(` + abc.xyz = "pqr"; + abc.ghi = {}; + abc.jkl = 3.14159; + abc.mno = mno; + mno.Abc = true; + mno.Ghi = "Something happens."; + [ abc.Xyzzy, abc.def, abc.ghi, abc.mno ]; + `, "Nothing happens.,1,[object Object],[object Object]") + + is(abc["xyz"], "pqr") + is(abc["ghi"], "[object Object]") + is(abc["jkl"], float64(3.14159)) + mno, valid := abc["mno"].(*testStruct) + is(valid, true) + is(mno.Abc, true) + is(mno.Ghi, "Something happens.") + } + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/regexp_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/regexp_test.go new file mode 100644 index 000000000..36183a0d9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/regexp_test.go @@ -0,0 +1,287 @@ +package otto + +import ( + "fmt" + "testing" +) + +func TestRegExp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + /abc/.toString(), + /abc/gim.toString(), + ""+/abc/gi.toString(), + new RegExp("1(\\d+)").toString(), + ]; + `, "/abc/,/abc/gim,/abc/gi,/1(\\d+)/") + + test(` + [ + new RegExp("abc").exec("123abc456"), + null === new RegExp("xyzzy").exec("123abc456"), + new RegExp("1(\\d+)").exec("123abc456"), + new RegExp("xyzzy").test("123abc456"), + new RegExp("1(\\d+)").test("123abc456"), + new RegExp("abc").exec("123abc456"), + ]; + `, "abc,true,123,23,false,true,abc") + + test(`new RegExp("abc").toString()`, "/abc/") + test(`new RegExp("abc", "g").toString()`, "/abc/g") + test(`new RegExp("abc", "mig").toString()`, "/abc/gim") + + result := test(`/(a)?/.exec('b')`, ",") + is(result._object().get("0"), "") + is(result._object().get("1"), "undefined") + is(result._object().get("length"), 2) + + result = test(`/(a)?(b)?/.exec('b')`, "b,,b") + is(result._object().get("0"), "b") + is(result._object().get("1"), "undefined") + is(result._object().get("2"), "b") + is(result._object().get("length"), 3) + + test(`/\u0041/.source`, "\\u0041") + test(`/\a/.source`, "\\a") + test(`/\;/.source`, "\\;") + + test(`/a\a/.source`, "a\\a") + test(`/,\;/.source`, ",\\;") + test(`/ \ /.source`, " \\ ") + + // Start sanity check... + test("eval(\"/abc/\").source", "abc") + test("eval(\"/\u0023/\").source", "#") + test("eval(\"/\u0058/\").source", "X") + test("eval(\"/\\\u0023/\").source == \"\\\u0023\"", true) + test("'0x' + '0058'", "0x0058") + test("'\\\\' + '0x' + '0058'", "\\0x0058") + // ...stop sanity check + + test(`abc = '\\' + String.fromCharCode('0x' + '0058'); eval('/' + abc + '/').source`, "\\X") + test(`abc = '\\' + String.fromCharCode('0x0058'); eval('/' + abc + '/').source == "\\\u0058"`, true) + test(`abc = '\\' + String.fromCharCode('0x0023'); eval('/' + abc + '/').source == "\\\u0023"`, true) + test(`abc = '\\' + String.fromCharCode('0x0078'); eval('/' + abc + '/').source == "\\\u0078"`, true) + + test(` + var abc = Object.getOwnPropertyDescriptor(RegExp, "prototype"); + [ [ typeof RegExp.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestRegExp_global(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /(?:ab|cd)\d?/g; + var found = []; + do { + match = abc.exec("ab cd2 ab34 cd"); + if (match !== null) { + found.push(match[0]); + } else { + break; + } + } while (true); + found; + `, "ab,cd2,ab3,cd") + }) +} + +func TestRegExp_exec(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = /./g; + def = '123456'; + ghi = 0; + while (ghi < 100 && abc.exec(def) !== null) { + ghi += 1; + } + [ ghi, def.length, ghi == def.length ]; + `, "6,6,true") + + test(` + abc = /[abc](\d)?/g; + def = 'a0 b c1 d3'; + ghi = 0; + lastIndex = 0; + while (ghi < 100 && abc.exec(def) !== null) { + lastIndex = abc.lastIndex; + ghi += 1; + + } + [ ghi, lastIndex ]; + `, "3,7") + + test(` + var abc = /[abc](\d)?/.exec("a0 b c1 d3"); + [ abc.length, abc.input, abc.index, abc ]; + `, "2,a0 b c1 d3,0,a0,0") + + test(`raise: + var exec = RegExp.prototype.exec; + exec("Xyzzy"); + `, "TypeError: Calling RegExp.exec on a non-RegExp object") + + test(` + var abc = /\w{3}\d?/.exec("CE\uFFFFL\uFFDDbox127"); + [ abc.input.length, abc.length, abc.input, abc.index, abc ]; + `, "11,1,CE\uFFFFL\uFFDDbox127,5,box1") + + test(`RegExp.prototype.exec.length`, 1) + test(`RegExp.prototype.exec.prototype`, "undefined") + }) +} + +func TestRegExp_test(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`RegExp.prototype.test.length`, 1) + test(`RegExp.prototype.test.prototype`, "undefined") + }) +} + +func TestRegExp_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`RegExp.prototype.toString.length`, 0) + test(`RegExp.prototype.toString.prototype`, "undefined") + }) +} + +func TestRegExp_zaacbbbcac(t *testing.T) { + return + tt(t, func() { + test, _ := test() + + // FIXME? TODO /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac") + test(` + var abc = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac"); + [ abc.length, abc.index, abc ]; + `, "6,0,zaacbbbcac,z,ac,a,,c") + }) +} + +func TestRegExpCopying(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = /xyzzy/i; + def = RegExp(abc); + abc.indicator = 1; + [ abc.indicator, def.indicator ]; + `, "1,1") + + test(`raise: + RegExp(new RegExp("\\d"), "1"); + `, "TypeError: Cannot supply flags when constructing one RegExp from another") + }) +} + +func TestRegExp_multiline(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /s$/m.exec("pairs\nmakes\tdouble"); + [ abc.length, abc.index, abc ]; + `, "1,4,s") + }) +} + +func TestRegExp_source(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ /xyzzy/i.source, /./i.source ]; + `, "xyzzy,.") + + test(` + var abc = /./i; + var def = new RegExp(abc); + [ abc.source, def.source, abc.source === def.source ]; + `, ".,.,true") + + test(` + var abc = /./i; + var def = abc.hasOwnProperty("source"); + var ghi = abc.source; + abc.source = "xyzzy"; + [ def, abc.source ]; + `, "true,.") + }) +} + +func TestRegExp_newRegExp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + Math.toString(); + var abc = new RegExp(Math,eval("\"g\"")); + [ abc, abc.global ]; + `, "/[object Math]/g,true") + }) +} + +func TestRegExp_flags(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /./i; + var def = new RegExp(abc); + [ abc.multiline == def.multiline, abc.global == def.global, abc.ignoreCase == def.ignoreCase ]; + `, "true,true,true") + }) +} + +func TestRegExp_controlCharacter(t *testing.T) { + tt(t, func() { + test, _ := test() + + for code := 0x41; code < 0x5a; code++ { + string_ := string(code - 64) + test(fmt.Sprintf(` + var code = 0x%x; + var string = String.fromCharCode(code %% 32); + var result = (new RegExp("\\c" + String.fromCharCode(code))).exec(string); + [ code, string, result ]; + `, code), fmt.Sprintf("%d,%s,%s", code, string_, string_)) + } + }) +} + +func TestRegExp_notNotEmptyCharacterClass(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /[\s\S]a/m.exec("a\naba"); + [ abc.length, abc.input, abc ]; + `, "1,a\naba,\na") + }) +} + +func TestRegExp_compile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /[\s\S]a/; + abc.compile('^\w+'); + `, "undefined") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/registry/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/registry/README.markdown new file mode 100644 index 000000000..ba2d38909 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/registry/README.markdown @@ -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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/registry/registry.go b/Godeps/_workspace/src/github.com/obscuren/otto/registry/registry.go new file mode 100644 index 000000000..966638ac4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/registry/registry.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/result.go b/Godeps/_workspace/src/github.com/obscuren/otto/result.go new file mode 100644 index 000000000..cf42c423a --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/result.go @@ -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} +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/runtime.go b/Godeps/_workspace/src/github.com/obscuren/otto/runtime.go new file mode 100644 index 000000000..76e51d78c --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/runtime.go @@ -0,0 +1,394 @@ +package otto + +import ( + "errors" + "reflect" + + "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 { + Stack [](*_executionContext) + + GlobalObject *_object + GlobalEnvironment *_objectEnvironment + + Global _global + + eval *_object // The builtin eval, for determine indirect versus direct invocation + + Otto *Otto + + labels []string // FIXME +} + +func (self *_runtime) EnterGlobalExecutionContext() { + self.EnterExecutionContext(newExecutionContext(self.GlobalEnvironment, self.GlobalEnvironment, self.GlobalObject)) +} + +func (self *_runtime) EnterExecutionContext(scope *_executionContext) { + self.Stack = append(self.Stack, scope) +} + +func (self *_runtime) LeaveExecutionContext() { + self.Stack = self.Stack[:len(self.Stack)-1] +} + +func (self *_runtime) _executionContext(depth int) *_executionContext { + if depth == 0 { + return self.Stack[len(self.Stack)-1] + } + if len(self.Stack)-1+depth >= 0 { + return self.Stack[len(self.Stack)-1+depth] + } + return nil +} + +func (self *_runtime) EnterFunctionExecutionContext(function *_object, this Value) *_functionEnvironment { + scopeEnvironment := function.functionValue().call.ScopeEnvironment() + if scopeEnvironment == nil { + scopeEnvironment = self.GlobalEnvironment + } + environment := self.newFunctionEnvironment(scopeEnvironment) + var thisObject *_object + switch this._valueType { + case valueUndefined, valueNull: + thisObject = self.GlobalObject + default: + thisObject = self.toObject(this) + } + self.EnterExecutionContext(newExecutionContext(environment, environment, thisObject)) + return environment +} + +func (self *_runtime) EnterEvalExecutionContext(call FunctionCall) { + // Skip the current function lexical/variable environment, which is of the function execution context call + // to eval (the global execution context). Instead, execute in the context of where the eval was called, + // which is essentially dynamic scoping + parent := self._executionContext(-1) + new := newExecutionContext(parent.LexicalEnvironment, parent.VariableEnvironment, parent.this) + // FIXME Make passing through of self.GlobalObject more general? Whenever newExecutionContext is passed a nil object? + new.eval = true + self.EnterExecutionContext(new) +} + +func (self *_runtime) GetValue(value Value) Value { + if value.isReference() { + return value.reference().GetValue() + } + return value +} + +func (self *_runtime) PutValue(reference _reference, value Value) { + if !reference.PutValue(value) { + // Why? -- If reference.Base == nil + strict := false + self.GlobalObject.defineProperty(reference.GetName(), value, 0111, strict) + } +} + +func (self *_runtime) Call(function *_object, this Value, argumentList []Value, evalHint bool) Value { + // Pass eval boolean through to EnterFunctionExecutionContext for further testing + _functionEnvironment := self.EnterFunctionExecutionContext(function, this) + defer func() { + self.LeaveExecutionContext() + }() + + if evalHint { + evalHint = function == self.eval // If evalHint is true, then it IS a direct eval + } + callValue := function.functionValue().call.Dispatch(function, _functionEnvironment, self, this, argumentList, evalHint) + if value, valid := callValue.value.(_result); valid { + return value.value + } + return callValue +} + +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())) + //case *_syntaxError: + // exception = true + // tryValue = toValue_object(self.newError("SyntaxError", toValue_string(caught.Message))) + case Value: + exception = true + tryValue = caught + default: + panic(caught) + } + } + }() + + tryValue = inner() + return +} + +// _executionContext Proxy + +func (self *_runtime) localGet(name string) Value { + return self._executionContext(0).getValue(name) +} + +func (self *_runtime) localSet(name string, value Value) { + self._executionContext(0).setValue(name, value, false) +} + +func (self *_runtime) VariableEnvironment() _environment { + return self._executionContext(0).VariableEnvironment +} + +func (self *_runtime) LexicalEnvironment() _environment { + return self._executionContext(0).LexicalEnvironment +} + +// toObject + +func (self *_runtime) toObject(value Value) *_object { + switch value._valueType { + case valueEmpty, valueUndefined, valueNull: + panic(newTypeError()) + case valueBoolean: + return self.newBoolean(value) + case valueString: + return self.newString(value) + case valueNumber: + return self.newNumber(value) + case valueObject: + return value._object() + } + panic(newTypeError()) +} + +func (self *_runtime) objectCoerce(value Value) (*_object, error) { + switch value._valueType { + 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(newTypeError()) +} + +func checkObjectCoercible(value Value) { + isObject, mustCoerce := testObjectCoercible(value) + if !isObject && !mustCoerce { + panic(newTypeError()) + } +} + +// testObjectCoercible + +func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) { + switch value._valueType { + case valueReference, valueEmpty, valueNull, valueUndefined: + return false, false + case valueNumber, valueString, valueBoolean: + isObject = false + mustCoerce = true + case valueObject: + isObject = true + mustCoerce = false + } + return +} + +func (self *_runtime) ToValue(value interface{}) (Value, error) { + result := UndefinedValue() + err := catchPanic(func() { + result = self.toValue(value) + }) + return result, err +} + +func (self *_runtime) toValue(value interface{}) Value { + switch value := value.(type) { + case Value: + return value + case func(FunctionCall) Value: + return toValue_object(self.newNativeFunction("", value)) + case _nativeFunction: + return toValue_object(self.newNativeFunction("", value)) + case Object, *Object, _object, *_object: + // Nothing happens. + // FIXME + 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.Func: + return toValue_object(self.newNativeFunction("", func(call FunctionCall) Value { + args := make([]reflect.Value, len(call.ArgumentList)) + for i, a := range call.ArgumentList { + args[i] = reflect.ValueOf(a.export()) + } + + retvals := value.Call(args) + if len(retvals) > 1 { + panic(newTypeError()) + } else if len(retvals) == 1 { + return toValue(retvals[0].Interface()) + } + return UndefinedValue() + })) + 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)) + } + } + } + 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 interface{}) (*ast.Program, error) { + return parser.ParseFile(nil, filename, src, 0) +} + +func (runtime *_runtime) cmpl_parse(filename string, src interface{}) (*_nodeProgram, error) { + program, err := parser.ParseFile(nil, filename, src, 0) + if err != nil { + return nil, err + } + return cmpl_parse(program), nil +} + +func (self *_runtime) parseSource(src 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) + return nil, program, err +} + +func (self *_runtime) cmpl_run(src interface{}) (Value, error) { + result := UndefinedValue() + cmpl_program, program, err := self.parseSource(src) + 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) + }) + switch result._valueType { + case valueEmpty: + result = UndefinedValue() + case valueReference: + result = self.GetValue(result) + } + return result, err +} + +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(newReferenceError(err.Message)) + } + panic(newSyntaxError(err.Message)) + } + } + panic(newSyntaxError(err.Error())) +} + +func (self *_runtime) parseOrThrow(source string) *ast.Program { + program, err := self.parse("", source) + self.parseThrow(err) // Will panic/throw appropriately + return program +} + +func (self *_runtime) cmpl_parseOrThrow(source string) *_nodeProgram { + program, err := self.cmpl_parse("", source) + self.parseThrow(err) // Will panic/throw appropriately + return program +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/runtime_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/runtime_test.go new file mode 100644 index 000000000..8706d59a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/runtime_test.go @@ -0,0 +1,778 @@ +package otto + +import ( + "math" + "testing" +) + +// FIXME terst, Review tests + +func TestOperator(t *testing.T) { + tt(t, func() { + test, vm := test() + + test("xyzzy = 1") + test("xyzzy", 1) + + if true { + vm.Set("twoPlusTwo", func(FunctionCall) Value { + return toValue(5) + }) + test("twoPlusTwo( 1 )", 5) + + test("1 + twoPlusTwo( 1 )", 6) + + test("-1 + twoPlusTwo( 1 )", 4) + } + + test("result = 4") + test("result", 4) + + test("result += 1") + test("result", 5) + + test("result *= 2") + test("result", 10) + + test("result /= 2") + test("result", 5) + + test("result = 112.51 % 3.1") + test("result", 0.9100000000000019) + + test("result = 'Xyzzy'") + test("result", "Xyzzy") + + test("result = 'Xyz' + 'zy'") + test("result", "Xyzzy") + + test("result = \"Xyzzy\"") + test("result", "Xyzzy") + + test("result = 1; result = result") + test("result", 1) + + test(` + var result64 + = + 64 + , result10 = + 10 + `) + test("result64", 64) + test("result10", 10) + + test(` + result = 1; + result += 1; + `) + test("result", 2) + }) +} + +func TestFunction_(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + result = 2 + xyzzy = function() { + result += 1 + } + xyzzy() + result; + `, 3) + + test(` + xyzzy = function() { + return 1 + } + result = xyzzy() + `, 1) + + test(` + xyzzy = function() {} + result = xyzzy() + `, "undefined") + + test(` + xyzzy = function() { + return 64 + return 1 + } + result = xyzzy() + `, 64) + + test(` + result = 4 + xyzzy = function() { + result = 2 + } + xyzzy(); + result; + `, 2) + + test(` + result = 4 + xyzzy = function() { + var result + result = 2 + } + xyzzy(); + result; + `, 4) + + test(` + xyzzy = function() { + var result = 4 + return result + } + result = xyzzy() + `, 4) + + test(` + xyzzy = function() { + function test() { + var result = 1 + return result + } + return test() + 1 + } + result = xyzzy() + 1 + `, 3) + + test(` + xyzzy = function() { + function test() { + var result = 1 + return result + } + _xyzzy = 2 + var result = _xyzzy + test() + 1 + return result + } + result = xyzzy() + 1; + [ result, _xyzzy ]; + `, "5,2") + + test(` + xyzzy = function(apple) { + return 1 + } + result = xyzzy(1) + `, 1) + + test(` + xyzzy = function(apple) { + return apple + 1 + } + result = xyzzy(2) + `, 3) + + test(` + { + result = 1 + result += 1; + } + `, 2) + + test(` + var global = 1 + outer = function() { + var global = 2 + var inner = function(){ + return global + } + return inner() + } + result = outer() + `, 2) + + test(` + var apple = 1 + var banana = function() { + return apple + } + var cherry = function() { + var apple = 2 + return banana() + } + result = cherry() + `, 1) + + test(` + function xyz() { + }; + delete xyz; + `, false) + + test(` + var abc = function __factorial(def){ + if (def === 1) { + return def; + } else { + return __factorial(def-1)*def; + } + }; + abc(3); + `, 6) + }) +} + +func TestDoWhile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4; + result = 0; + do { + result = result + 1; + limit = limit - 1; + } while (limit); + result; + `, 4) + + test(` + result = eval("do {abc=1; break; abc=2;} while (0);"); + [ result, abc ]; + `, "1,1") + }) +} + +func TestContinueBreak(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + } + else { + break + } + result = result + 1 + } + [ result, limit ]; + `, "3,0") + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + continue + } + else { + break + } + result = result + 1 + } + result; + `, 0) + + test(` + limit = 4 + result = 0 + do { + limit = limit - 1 + if (limit) { + continue + } + else { + break + } + result = result + 1 + } while (limit) + result; + `, 0) + }) +} + +func TestTryCatchError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc + try { + 1() + } + catch (def) { + abc = def + } + abc; + `, "TypeError: 1 is not a function") + + }) +} + +func TestPositiveNegativeZero(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`1/0`, _Infinity) + test(`1/-0`, -_Infinity) + test(` + abc = -0 + 1/abc + `, -_Infinity) + }) +} + +func TestComparison(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + undefined = 1; undefined; + `, "undefined") + + test("undefined == undefined", true) + + test("undefined != undefined", false) + + test("null == null", true) + + test("null != null", false) + + test("0 == 1", false) + + is(negativeZero(), -0) + is(positiveZero(), 0) + is(math.Signbit(negativeZero()), true) + is(positiveZero() == negativeZero(), true) + + test("1 == 1", true) + + test("'Hello, World.' == 'Goodbye, World.'", false) + + test("'Hello, World.' == true", false) + + test("'Hello, World.' == false", false) + + test("'Hello, World.' == 1", false) + + test("1 == 'Hello, World.'", false) + + is(stringToFloat("-1"), -1) + + test("0+Object", "0function Object() { [native code] }") + }) +} + +func TestComparisonRelational(t *testing.T) { + tt(t, func() { + test, _ := test() + + test("0 < 0", false) + + test("0 > 0", false) + + test("0 <= 0", true) + + test("0 >= 0", true) + + test("' 0' >= 0", true) + + test("'_ 0' >= 0", false) + }) +} + +func TestArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + xyzzy = function() { + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "xyzzy") + + test(` + xyzzy = function() { + arguments[0] = "abcdef" + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "abcdef") + + test(` + xyzzy = function(apple) { + apple = "abcdef" + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "abcdef") + + test(` + (function(){ + return arguments + })() + `, "[object Arguments]") + + test(` + (function(){ + return arguments.length + })() + `, 0) + + test(` + (function(){ + return arguments.length + })(1, 2, 4, 8, 10) + `, 5) + }) +} + +func TestObjectLiteral(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + ({}); + `, "[object Object]") + + test(` + var abc = { + xyzzy: "Nothing happens.", + get 1e2() { + return 3.14159; + }, + get null() { + return true; + }, + get "[\n]"() { + return "<>"; + } + }; + [ abc["1e2"], abc.null, abc["[\n]"] ]; + `, "3.14159,true,<>") + + test(` + var abc = { + xyzzy: "Nothing happens.", + set 1e2() { + this[3.14159] = 100; + return Math.random(); + }, + set null(def) { + this.def = def; + return Math.random(); + }, + }; + [ abc["1e2"] = Infinity, abc[3.14159], abc.null = "xyz", abc.def ]; + `, "Infinity,100,xyz,xyz") + }) +} + +func TestUnaryPrefix(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var result = 0; + [++result, result]; + `, "1,1") + + test(` + result = 0; + [--result, result]; + `, "-1,-1") + + test(` + var object = { valueOf: function() { return 1; } }; + result = ++object; + [ result, typeof result ]; + `, "2,number") + + test(` + var object = { valueOf: function() { return 1; } }; + result = --object; + [ result, typeof result ]; + `, "0,number") + }) +} + +func TestUnaryPostfix(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var result = 0; + result++; + [ result++, result ]; + `, "1,2") + + test(` + result = 0; + result--; + [ result--, result ]; + `, "-1,-2") + + test(` + var object = { valueOf: function() { return 1; } }; + result = object++; + [ result, typeof result ]; + `, "1,number") + + test(` + var object = { valueOf: function() { return 1; } }; + result = object-- + [ result, typeof result ]; + `, "1,number") + }) +} + +func TestBinaryLogicalOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = true + def = false + ghi = false + jkl = false + result = abc && def || ghi && jkl + `, false) + + test(` + abc = true + def = true + ghi = false + jkl = false + result = abc && def || ghi && jkl + `, true) + + }) +} + +func TestBinaryBitwiseOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = 1 & 2; + def = 1 & 3; + ghi = 1 | 3; + jkl = 1 ^ 2; + mno = 1 ^ 3; + [ abc, def, ghi, jkl, mno ]; + `, "0,1,3,3,2") + }) +} + +func TestBinaryShiftOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + high = (1 << 30) - 1 + (1 << 30) + low = -high - 1 + abc = 23 << 1 + def = -105 >> 1 + ghi = 23 << 2 + jkl = 1 >>> 31 + mno = 1 << 64 + pqr = 1 >> 2 + stu = -2 >> 4 + vwx = low >> 1 + yz = low >>> 1 + `) + test("abc", 46) + test("def", -53) + test("ghi", 92) + test("jkl", 0) + test("mno", 1) + test("pqr", 0) + test("stu", -1) + test("vwx", -1073741824) + test("yz", 1073741824) + }) +} + +func TestParenthesizing(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = 1 + 2 * 3 + def = (1 + 2) * 3 + ghi = !(false || true) + jkl = !false || true + `) + test("abc", 7) + test("def", 9) + test("ghi", false) + test("jkl", true) + }) +} + +func Test_instanceof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = {} instanceof Object; + `, true) + + test(` + abc = "abc" instanceof Object; + `, false) + + test(`raise: + abc = {} instanceof "abc"; + `, "TypeError: Expecting a function in instanceof check, but got: abc") + + test(`raise: + "xyzzy" instanceof Math; + `, "TypeError") + }) +} + +func TestIn(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "prototype" in Object; + def = "xyzzy" in Object; + [ abc, def ]; + `, "true,false") + }) +} + +func Test_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = new Boolean; + def = new Boolean(1); + [ abc, def ]; + `, "false,true") + }) +} + +func TestNewFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + new Function("return 11")() + `, 11) + + test(` + abc = 10 + new Function("abc += 1")() + abc + `, 11) + + test(` + new Function("a", "b", "c", "return b + 2")(10, 11, 12) + `, 13) + + test(`raise: + new 1 + `, "TypeError: 1 is not a function") + + // TODO Better error reporting: new this + test(`raise: + new this + `, "TypeError: [object environment] is not a function") + + test(`raise: + new {} + `, "TypeError: [object Object] is not a function") + }) +} + +func TestNewPrototype(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = { 'xyzzy': 'Nothing happens.' } + function Xyzzy(){} + Xyzzy.prototype = abc; + (new Xyzzy()).xyzzy + `, "Nothing happens.") + }) +} + +func TestBlock(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc=0; + var ghi; + def: { + do { + abc++; + if (!(abc < 10)) { + break def; + ghi = "ghi"; + } + } while (true); + } + [ abc,ghi ]; + `, "10,") + }) +} + +func Test_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [undefined+""] + `, "undefined") + }) +} + +func TestEvaluationOrder(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + abc < (abc = 1) === true; + `, true) + }) +} + +func TestClone(t *testing.T) { + tt(t, func() { + vm1 := New() + vm1.Run(` + var abc = 1; + `) + + vm2 := vm1.clone() + vm1.Run(` + abc += 2; + `) + vm2.Run(` + abc += 4; + `) + + is(vm1.getValue("abc"), 3) + is(vm2.getValue("abc"), 5) + }) +} + +func Test_debugger(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + debugger; + `, "undefined") + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/script.go b/Godeps/_workspace/src/github.com/obscuren/otto/script.go new file mode 100644 index 000000000..ed8aebbf4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/script.go @@ -0,0 +1,122 @@ +package otto + +import ( + "bytes" + "encoding/gob" + "errors" + + "github.com/robertkrimen/otto/parser" +) + +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) { + { + src, err := parser.ReadSource(filename, src) + if err != nil { + return nil, err + } + + program, err := self.runtime.parse(filename, src) + if err != nil { + return nil, err + } + + cmpl_program := cmpl_parse(program) + + script := &Script{ + version: scriptVersion, + program: cmpl_program, + filename: filename, + src: string(src), + } + + 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/script_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/script_test.go new file mode 100644 index 000000000..1a25b8a95 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/script_test.go @@ -0,0 +1,76 @@ +package otto + +import ( + "testing" +) + +func TestScript(t *testing.T) { + tt(t, func() { + return + + vm := New() + + script, err := vm.Compile("xyzzy", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + is(err, nil) + + str := script.String() + is(str, "// xyzzy\nvar abc; if (!abc) abc = 0; abc += 2; abc;") + + value, err := vm.Run(script) + is(err, nil) + is(value, 2) + + tmp, err := script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + + { + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, nil) + + is(script.String(), str) + + value, err = vm.Run(script) + is(err, nil) + is(value, 4) + + tmp, err = script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + } + + { + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, nil) + + is(script.String(), str) + + value, err := vm.Run(script) + is(err, nil) + is(value, 6) + + tmp, err = script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + } + + { + version := scriptVersion + scriptVersion = "bogus" + + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, "version mismatch") + + is(script.String(), "// \n") + is(script.version, "") + is(script.program == nil, true) + is(script.filename, "") + is(script.src, "") + + scriptVersion = version + } + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/string_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/string_test.go new file mode 100644 index 000000000..b7b06324b --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/string_test.go @@ -0,0 +1,365 @@ +package otto + +import ( + "testing" +) + +func TestString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = (new String("xyzzy")).length; + def = new String().length; + ghi = new String("Nothing happens.").length; + `) + test("abc", 5) + test("def", 0) + test("ghi", 16) + test(`"".length`, 0) + test(`"a\uFFFFbc".length`, 4) + test(`String(+0)`, "0") + test(`String(-0)`, "0") + test(`""+-0`, "0") + test(` + var abc = Object.getOwnPropertyDescriptor(String, "prototype"); + [ [ typeof String.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestString_charAt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "xyzzy".charAt(0) + def = "xyzzy".charAt(11) + `) + test("abc", "x") + test("def", "") + }) +} + +func TestString_charCodeAt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "xyzzy".charCodeAt(0) + def = "xyzzy".charCodeAt(11) + `) + test("abc", 120) + test("def", _NaN) + }) +} + +func TestString_fromCharCode(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`String.fromCharCode()`, []uint16{}) + test(`String.fromCharCode(88, 121, 122, 122, 121)`, []uint16{88, 121, 122, 122, 121}) // FIXME terst, Double-check these... + test(`String.fromCharCode("88", 121, 122, 122.05, 121)`, []uint16{88, 121, 122, 122, 121}) + test(`String.fromCharCode("88", 121, 122, NaN, 121)`, []uint16{88, 121, 122, 0, 121}) + test(`String.fromCharCode("0x21")`, []uint16{33}) + test(`String.fromCharCode(-1).charCodeAt(0)`, 65535) + test(`String.fromCharCode(65535).charCodeAt(0)`, 65535) + test(`String.fromCharCode(65534).charCodeAt(0)`, 65534) + test(`String.fromCharCode(4294967295).charCodeAt(0)`, 65535) + test(`String.fromCharCode(4294967294).charCodeAt(0)`, 65534) + test(`String.fromCharCode(0x0024) === "$"`, true) + }) +} + +func TestString_concat(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".concat()`, "") + test(`"".concat("abc", "def")`, "abcdef") + test(`"".concat("abc", undefined, "def")`, "abcundefineddef") + }) +} + +func TestString_indexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".indexOf("")`, 0) + test(`"".indexOf("", 11)`, 0) + test(`"abc".indexOf("")`, 0) + test(`"abc".indexOf("", 11)`, 3) + test(`"abc".indexOf("a")`, 0) + test(`"abc".indexOf("bc")`, 1) + test(`"abc".indexOf("bc", 11)`, -1) + test(`"$$abcdabcd".indexOf("ab", function(){return -Infinity;}())`, 2) + test(`"$$abcdabcd".indexOf("ab", function(){return NaN;}())`, 2) + + test(` + var abc = {toString:function(){return "\u0041B";}} + var def = {valueOf:function(){return true;}} + var ghi = "ABB\u0041BABAB"; + var jkl; + with(ghi) { + jkl = indexOf(abc, def); + } + jkl; + `, 3) + }) +} + +func TestString_lastIndexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".lastIndexOf("")`, 0) + test(`"".lastIndexOf("", 11)`, 0) + test(`"abc".lastIndexOf("")`, 3) + test(`"abc".lastIndexOf("", 11)`, 3) + test(`"abc".lastIndexOf("a")`, 0) + test(`"abc".lastIndexOf("bc")`, 1) + test(`"abc".lastIndexOf("bc", 11)`, 1) + test(`"abc".lastIndexOf("bc", 0)`, -1) + test(`"abc".lastIndexOf("abcabcabc", 2)`, -1) + test(`"abc".lastIndexOf("abc", 0)`, 0) + test(`"abc".lastIndexOf("abc", 1)`, 0) + test(`"abc".lastIndexOf("abc", 2)`, 0) + test(`"abc".lastIndexOf("abc", 3)`, 0) + + test(` + abc = new Object(true); + abc.lastIndexOf = String.prototype.lastIndexOf; + abc.lastIndexOf(true, false); + `, 0) + }) +} + +func TestString_match(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc____abc_abc___".match(/__abc/)`, "__abc") + test(`"abc___abc_abc__abc__abc".match(/abc/g)`, "abc,abc,abc,abc,abc") + test(`"abc____abc_abc___".match(/__abc/g)`, "__abc") + test(` + abc = /abc/g + "abc___abc_abc__abc__abc".match(abc) + `, "abc,abc,abc,abc,abc") + test(`abc.lastIndex`, 23) + }) +} + +func TestString_replace(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc_abc".replace(/abc/, "$&123")`, "abc123_abc") + test(`"abc_abc".replace(/abc/g, "$&123")`, "abc123_abc123") + test(`"abc_abc_".replace(/abc/g, "$&123")`, "abc123_abc123_") + test(`"_abc_abc_".replace(/abc/g, "$&123")`, "_abc123_abc123_") + test(`"abc".replace(/abc/, "$&123")`, "abc123") + test(`"abc_".replace(/abc/, "$&123")`, "abc123_") + test("\"^abc$\".replace(/abc/, \"$`def\")", "^^def$") + test("\"^abc$\".replace(/abc/, \"def$`\")", "^def^$") + test(`"_abc_abd_".replace(/ab(c|d)/g, "$1")`, "_c_d_") + test(` + "_abc_abd_".replace(/ab(c|d)/g, function(){ + }) + `, "_undefined_undefined_") + + test(`"b".replace(/(a)?(b)?/, "_$1_")`, "__") + test(` + "b".replace(/(a)?(b)?/, function(a, b, c, d, e, f){ + return [a, b, c, d, e, f] + }) + `, "b,,b,0,b,") + + test(` + var abc = 'She sells seashells by the seashore.'; + var def = /sh/; + [ abc.replace(def, "$'" + 'sch') ]; + `, "She sells seaells by the seashore.schells by the seashore.") + }) +} + +func TestString_search(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".search(/abc/)`, 0) + test(`"abc".search(/def/)`, -1) + test(`"abc".search(/c$/)`, 2) + test(`"abc".search(/$/)`, 3) + }) +} + +func TestString_split(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".split("", 1)`, "a") + test(`"abc".split("", 2)`, "a,b") + test(`"abc".split("", 3)`, "a,b,c") + test(`"abc".split("", 4)`, "a,b,c") + test(`"abc".split("", 11)`, "a,b,c") + test(`"abc".split("", 0)`, "") + test(`"abc".split("")`, "a,b,c") + + test(`"abc".split(undefined)`, "abc") + + test(`"__1__3_1__2__".split("_")`, ",,1,,3,1,,2,,") + + test(`"__1__3_1__2__".split(/_/)`, ",,1,,3,1,,2,,") + + test(`"ab".split(/a*/)`, ",b") + + test(`_ = "Aboldandcoded".split(/<(\/)?([^<>]+)>/)`, "A,,B,bold,/,B,and,,CODE,coded,/,CODE,") + test(`_.length`, 13) + test(`_[1] === undefined`, true) + test(`_[12] === ""`, true) + + test(` + var abc = new String("one-1 two-2 three-3"); + var def = abc.split(new RegExp); + + [ def.constructor === Array, abc.length, def.length, def.join('') ]; + `, "true,19,19,one-1 two-2 three-3") + }) +} + +func TestString_slice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".slice()`, "abc") + test(`"abc".slice(0)`, "abc") + test(`"abc".slice(0,11)`, "abc") + test(`"abc".slice(0,-1)`, "ab") + test(`"abc".slice(-1,11)`, "c") + test(`abc = "abc"; abc.slice(abc.length+1, 0)`, "") + }) +} + +func TestString_substring(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".substring()`, "abc") + test(`"abc".substring(0)`, "abc") + test(`"abc".substring(0,11)`, "abc") + test(`"abc".substring(11,0)`, "abc") + test(`"abc".substring(0,-1)`, "") + test(`"abc".substring(-1,11)`, "abc") + test(`"abc".substring(11,1)`, "bc") + test(`"abc".substring(1)`, "bc") + test(`"abc".substring(Infinity, Infinity)`, "") + }) +} + +func TestString_toCase(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".toLowerCase()`, "abc") + test(`"ABC".toLowerCase()`, "abc") + test(`"abc".toLocaleLowerCase()`, "abc") + test(`"ABC".toLocaleLowerCase()`, "abc") + test(`"abc".toUpperCase()`, "ABC") + test(`"ABC".toUpperCase()`, "ABC") + test(`"abc".toLocaleUpperCase()`, "ABC") + test(`"ABC".toLocaleUpperCase()`, "ABC") + }) +} + +func Test_floatToString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`String(-1234567890)`, "-1234567890") + test(`-+String(-(-1234567890))`, -1234567890) + test(`String(-1e128)`, "-1e+128") + test(`String(0.12345)`, "0.12345") + test(`String(-0.00000012345)`, "-1.2345e-7") + test(`String(0.0000012345)`, "0.0000012345") + test(`String(1000000000000000000000)`, "1e+21") + test(`String(1e21)`, "1e+21") + test(`String(1E21)`, "1e+21") + test(`String(-1000000000000000000000)`, "-1e+21") + test(`String(-1e21)`, "-1e+21") + test(`String(-1E21)`, "-1e+21") + test(`String(0.0000001)`, "1e-7") + test(`String(1e-7)`, "1e-7") + test(`String(1E-7)`, "1e-7") + test(`String(-0.0000001)`, "-1e-7") + test(`String(-1e-7)`, "-1e-7") + test(`String(-1E-7)`, "-1e-7") + }) +} + +func TestString_indexing(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Actually a test of stringToArrayIndex, under the hood. + test(` + abc = new String("abc"); + index = Math.pow(2, 32); + [ abc.length, abc[index], abc[index+1], abc[index+2], abc[index+3] ]; + `, "3,,,,") + }) +} + +func TestString_trim(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`' \n abc \t \n'.trim();`, "abc") + test(`" abc\u000B".trim()`, "abc") + test(`"abc ".trim()`, "abc") + test(` + var a = "\u180Eabc \u000B " + var b = a.trim() + a.length + b.length + `, 10) + }) +} + +func TestString_trimLeft(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimLeft()`, "abc\u000B") + test(`"abc ".trimLeft()`, "abc ") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimLeft() + a.length + b.length + `, 13) + }) +} + +func TestString_trimRight(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimRight()`, " abc") + test(`" abc ".trimRight()`, " abc") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimRight() + a.length + b.length + `, 11) + }) +} + +func TestString_localeCompare(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`'a'.localeCompare('c');`, -1) + test(`'c'.localeCompare('a');`, 1) + test(`'a'.localeCompare('a');`, 0) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/terst/terst.go b/Godeps/_workspace/src/github.com/obscuren/otto/terst/terst.go new file mode 100644 index 000000000..a25ca8b9c --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/terst/terst.go @@ -0,0 +1,669 @@ +// This file was AUTOMATICALLY GENERATED by terst-import (smuggol) from github.com/robertkrimen/terst + +/* +Package terst is a terse (terst = test + terse), easy-to-use testing library for Go. + +terst is compatible with (and works via) the standard testing package: http://golang.org/pkg/testing + + var is = terst.Is + + func Test(t *testing.T) { + terst.Terst(t, func() { + is("abc", "abc") + + is(1, ">", 0) + + var abc []int + is(abc, nil) + } + } + +Do not import terst directly, instead use `terst-import` to copy it into your testing environment: + +https://github.com/robertkrimen/terst/tree/master/terst-import + + $ go get github.com/robertkrimen/terst/terst-import + + $ terst-import + +*/ +package terst + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "testing" + "time" +) + +// Is compares two values (got & expect) and returns true if the comparison is true, +// false otherwise. In addition, if the comparison is false, Is will report the error +// in a manner similar to testing.T.Error(...). Is also takes an optional argument, +// a comparator, that changes how the comparison is made. The following +// comparators are available: +// +// == # got == expect (default) +// != # got != expect +// +// > # got > expect (float32, uint, uint16, int, int64, ...) +// >= # got >= expect +// < # got < expect +// <= # got <= expect +// +// =~ # regexp.MustCompile(expect).Match{String}(got) +// !~ # !regexp.MustCompile(expect).Match{String}(got) +// +// Basic usage with the default comparator (==): +// +// Is(, ) +// +// Specifying a different comparator: +// +// Is(, , ) +// +// A simple comparison: +// +// Is(2 + 2, 4) +// +// A bit trickier: +// +// Is(1, ">", 0) +// Is(2 + 2, "!=", 5) +// Is("Nothing happens.", "=~", `ing(\s+)happens\.$`) +// +// Is should only be called under a Terst(t, ...) call. For a standalone version, +// use IsErr. If no scope is found and the comparison is false, then Is will panic the error. +// +func Is(arguments ...interface{}) bool { + err := IsErr(arguments...) + if err != nil { + call := Caller() + if call == nil { + panic(err) + } + call.Error(err) + return false + } + return true +} + +type ( + // ErrFail indicates a comparison failure (e.g. 0 > 1). + ErrFail error + + // ErrInvalid indicates an invalid comparison (e.g. bool == string). + ErrInvalid error +) + +var errInvalid = errors.New("invalid") + +var registry = struct { + table map[uintptr]*_scope + lock sync.RWMutex +}{ + table: map[uintptr]*_scope{}, +} + +func registerScope(pc uintptr, scope *_scope) { + registry.lock.Lock() + defer registry.lock.Unlock() + registry.table[pc] = scope +} + +func scope() *_scope { + scope, _ := findScope() + return scope +} + +func floatCompare(a float64, b float64) int { + if a > b { + return 1 + } else if a < b { + return -1 + } + // NaN == NaN + return 0 +} + +func bigIntCompare(a *big.Int, b *big.Int) int { + return a.Cmp(b) +} + +func bigInt(value int64) *big.Int { + return big.NewInt(value) +} + +func bigUint(value uint64) *big.Int { + return big.NewInt(0).SetUint64(value) +} + +type _toString interface { + String() string +} + +func toString(value interface{}) (string, error) { + switch value := value.(type) { + case string: + return value, nil + case _toString: + return value.String(), nil + case error: + return value.Error(), nil + } + return "", errInvalid +} + +func matchString(got string, expect *regexp.Regexp) (int, error) { + if expect.MatchString(got) { + return 0, nil + } + return -1, nil +} + +func match(got []byte, expect *regexp.Regexp) (int, error) { + if expect.Match(got) { + return 0, nil + } + return -1, nil +} + +func compareMatch(got, expect interface{}) (int, error) { + switch got := got.(type) { + case []byte: + switch expect := expect.(type) { + case string: + matcher, err := regexp.Compile(expect) + if err != nil { + return 0, err + } + return match(got, matcher) + case *regexp.Regexp: + return match(got, expect) + } + default: + if got, err := toString(got); err == nil { + switch expect := expect.(type) { + case string: + matcher, err := regexp.Compile(expect) + if err != nil { + return 0, err + } + return matchString(got, matcher) + case *regexp.Regexp: + return matchString(got, expect) + } + } else { + return 0, err + } + } + return 0, errInvalid +} + +func floatPromote(value reflect.Value) (float64, error) { + kind := value.Kind() + if reflect.Int <= kind && kind <= reflect.Int64 { + return float64(value.Int()), nil + } + if reflect.Uint <= kind && kind <= reflect.Uint64 { + return float64(value.Uint()), nil + } + if reflect.Float32 <= kind && kind <= reflect.Float64 { + return value.Float(), nil + } + return 0, errInvalid +} + +func bigIntPromote(value reflect.Value) (*big.Int, error) { + kind := value.Kind() + if reflect.Int <= kind && kind <= reflect.Int64 { + return bigInt(value.Int()), nil + } + if reflect.Uint <= kind && kind <= reflect.Uint64 { + return bigUint(value.Uint()), nil + } + return nil, errInvalid +} + +func compareOther(got, expect interface{}) (int, error) { + { + switch expect.(type) { + case float32, float64: + return compareNumber(got, expect) + case uint, uint8, uint16, uint32, uint64: + return compareNumber(got, expect) + case int, int8, int16, int32, int64: + return compareNumber(got, expect) + case string: + var err error + got, err = toString(got) + if err != nil { + return 0, err + } + case nil: + got := reflect.ValueOf(got) + switch got.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface: + if got.IsNil() { + return 0, nil + } + return -1, nil + case reflect.Invalid: // reflect.Invalid: var abc interface{} = nil + return 0, nil + } + return 0, errInvalid + } + } + + if reflect.ValueOf(got).Type() != reflect.ValueOf(expect).Type() { + return 0, errInvalid + } + + if reflect.DeepEqual(got, expect) { + return 0, nil + } + return -1, nil +} + +func compareNumber(got, expect interface{}) (int, error) { + { + got := reflect.ValueOf(got) + k0 := got.Kind() + expect := reflect.ValueOf(expect) + k1 := expect.Kind() + if reflect.Float32 <= k0 && k0 <= reflect.Float64 || + reflect.Float32 <= k1 && k1 <= reflect.Float64 { + got, err := floatPromote(got) + if err != nil { + return 0, err + } + expect, err := floatPromote(expect) + if err != nil { + return 0, err + } + return floatCompare(got, expect), nil + } else { + got, err := bigIntPromote(got) + if err != nil { + return 0, err + } + expect, err := bigIntPromote(expect) + if err != nil { + return 0, err + } + return got.Cmp(expect), nil + } + } + + return 0, errInvalid +} + +// IsErr compares two values (got & expect) and returns nil if the comparison is true, an ErrFail if +// the comparison is false, or an ErrInvalid if the comparison is invalid. IsErr also +// takes an optional argument, a comparator, that changes how the comparison is made. +// +// Is & IsErr are similar but different: +// +// Is(...) // Should only be called within a Terst(...) call +// IsErr(...) // A standalone comparator, the same as Is, just without the automatic reporting +// +func IsErr(arguments ...interface{}) error { + var got, expect interface{} + comparator := "==" + switch len(arguments) { + case 0, 1: + return fmt.Errorf("invalid number of arguments to IsErr: %d", len(arguments)) + case 2: + got, expect = arguments[0], arguments[1] + default: + if value, ok := arguments[1].(string); ok { + comparator = value + } else { + return fmt.Errorf("invalid comparator: %v", arguments[1]) + } + got, expect = arguments[0], arguments[2] + } + + var result int + var err error + + switch comparator { + case "<", "<=", ">", ">=": + result, err = compareNumber(got, expect) + case "=~", "!~": + result, err = compareMatch(got, expect) + case "==", "!=": + result, err = compareOther(got, expect) + default: + return fmt.Errorf("invalid comparator: %s", comparator) + } + + if err == errInvalid { + return ErrInvalid(fmt.Errorf( + "\nINVALID (%s):\n got: %v (%T)\n expected: %v (%T)", + comparator, + got, got, + expect, expect, + )) + } else if err != nil { + return err + } + + equality, pass := false, false + + switch comparator { + case "==", "=~": + equality = true + pass = result == 0 + case "!=", "!~": + equality = true + pass = result != 0 + case "<": + pass = result < 0 + case "<=": + pass = result <= 0 + case ">": + pass = result > 0 + case ">=": + pass = result >= 0 + } + + if !pass { + if equality { + if comparator[1] == '~' { + if value, ok := got.([]byte); ok { + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %s %v%s\nexpected: %v%s", + comparator, + value, got, typeKindString(got), + expect, typeKindString(expect), + )) + } + } + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %v%s\nexpected: %v%s", + comparator, + got, typeKindString(got), + expect, typeKindString(expect), + )) + } + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %v%s\nexpected: %s %v%s", + comparator, + got, typeKindString(got), + comparator, expect, typeKindString(expect), + )) + } + + return nil +} + +func typeKindString(value interface{}) string { + reflectValue := reflect.ValueOf(value) + kind := reflectValue.Kind().String() + result := fmt.Sprintf("%T", value) + if kind == result { + if kind == "string" { + return "" + } + return fmt.Sprintf(" (%T)", value) + } + return fmt.Sprintf(" (%T=%s)", value, kind) +} + +func (scope *_scope) reset() { + scope.name = "" + scope.output = scope.output[:] + scope.start = time.Time{} + scope.duration = 0 +} + +// Terst creates a testing scope, where Is can be called and errors will be reported +// according to the top-level location of the comparison, and not where the Is call +// actually takes place. For example: +// +// func test(value int) { +// Is(value, 5) // <--- This failure is reported below. +// } +// +// Terst(t, func(){ +// +// Is(2, ">", 3) // <--- An error is reported here. +// +// test(5) // <--- An error is reported here. +// +// }) +// +func Terst(t *testing.T, arguments ...func()) { + scope := &_scope{ + t: t, + } + + pc, _, _, ok := runtime.Caller(1) // TODO Associate with the Test... func + if !ok { + panic("Here be dragons.") + } + + _, scope.testFunc = findTestFunc() + + registerScope(pc, scope) + + for _, fn := range arguments { + func() { + scope.reset() + name := scope.testFunc.Name() + index := strings.LastIndex(scope.testFunc.Name(), ".") + if index >= 0 { + name = name[index+1:] + "(Terst)" + } else { + name = "(Terst)" + } + name = "(Terst)" + scope.name = name + scope.start = time.Now() + defer func() { + scope.duration = time.Now().Sub(scope.start) + if err := recover(); err != nil { + scope.t.Fail() + scope.report() + panic(err) + } + scope.report() + }() + fn() + }() + } +} + +// From "testing" +func (scope *_scope) report() { + format := "~~~ %s: (Terst)\n%s" + if scope.t.Failed() { + fmt.Printf(format, "FAIL", scope.output) + } else if testing.Verbose() && len(scope.output) > 0 { + fmt.Printf(format, "PASS", scope.output) + } +} + +func (scope *_scope) log(call _entry, str string) { + scope.mu.Lock() + defer scope.mu.Unlock() + scope.output = append(scope.output, decorate(call, str)...) +} + +// decorate prefixes the string with the file and line of the call site +// and inserts the final newline if needed and indentation tabs for formascing. +func decorate(call _entry, s string) string { + + file, line := call.File, call.Line + if call.PC > 0 { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + fmt.Fprintf(buf, "%s:%d: ", file, line) + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] + } + for i, line := range lines { + if i > 0 { + // Second and subsequent lines are indented an extra tab. + buf.WriteString("\n\t\t") + } + buf.WriteString(line) + } + buf.WriteByte('\n') + return buf.String() +} + +func findScope() (*_scope, _entry) { + registry.lock.RLock() + defer registry.lock.RUnlock() + table := registry.table + depth := 2 // Starting depth + call := _entry{} + for { + pc, _, _, ok := runtime.Caller(depth) + if !ok { + break + } + if scope, exists := table[pc]; exists { + pc, file, line, _ := runtime.Caller(depth - 3) // Terst(...) + func(){}() + fn() => ???() + call.PC = pc + call.File = file + call.Line = line + return scope, call + } + depth++ + } + return nil, _entry{} +} + +// Call is a reference to a line immediately under a Terst testing scope. +type Call struct { + scope *_scope + entry _entry +} + +// Caller will search the stack, looking for a Terst testing scope. If a scope +// is found, then Caller returns a Call for logging errors, accessing testing.T, etc. +// If no scope is found, Caller returns nil. +func Caller() *Call { + scope, entry := findScope() + if scope == nil { + return nil + } + return &Call{ + scope: scope, + entry: entry, + } +} + +// TestFunc returns the *runtime.Func entry for the top-level Test...(t testing.T) +// function. +func (cl *Call) TestFunc() *runtime.Func { + return cl.scope.testFunc +} + +// T returns the original testing.T passed to Terst(...) +func (cl *Call) T() *testing.T { + return cl.scope.t +} + +// Log is the terst version of `testing.T.Log` +func (cl *Call) Log(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) +} + +// Logf is the terst version of `testing.T.Logf` +func (cl *Call) Logf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) +} + +// Error is the terst version of `testing.T.Error` +func (cl *Call) Error(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) + cl.scope.t.Fail() +} + +// Errorf is the terst version of `testing.T.Errorf` +func (cl *Call) Errorf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) + cl.scope.t.Fail() +} + +// Skip is the terst version of `testing.T.Skip` +func (cl *Call) Skip(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) + cl.scope.t.SkipNow() +} + +// Skipf is the terst version of `testing.T.Skipf` +func (cl *Call) Skipf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) + cl.scope.t.SkipNow() +} + +type _scope struct { + t *testing.T + testFunc *runtime.Func + name string + mu sync.RWMutex + output []byte + start time.Time + duration time.Duration +} + +type _entry struct { + PC uintptr + File string + Line int + Func *runtime.Func +} + +func _findFunc(match string) (_entry, *runtime.Func) { + depth := 2 // Starting depth + for { + pc, file, line, ok := runtime.Caller(depth) + if !ok { + break + } + fn := runtime.FuncForPC(pc) + name := fn.Name() + if index := strings.LastIndex(name, match); index >= 0 { + // Assume we have an instance of TestXyzzy in a _test file + return _entry{ + PC: pc, + File: file, + Line: line, + Func: fn, + }, fn + } + depth++ + } + return _entry{}, nil +} + +func findTestFunc() (_entry, *runtime.Func) { + return _findFunc(".Test") +} + +func findTerstFunc() (_entry, *runtime.Func) { + return _findFunc(".Terst") +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/test/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/test/Makefile new file mode 100644 index 000000000..ac76fdeac --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/test/Makefile @@ -0,0 +1,26 @@ +.PHONY: test fetch clean build err report + +TESTER := tester + +test: $(TESTER) + for test in test-*.js; do ./$^ -test=true $$test 1>/dev/null || exit 1; done + @echo PASS + +report: $(TESTER) + ./$^ -report | grep -v "MT READY" + +fetch: $(TESTER) + ./$^ fetch + +build: + go build -a -o $(TESTER) + +$(TESTER): tester.go + $(MAKE) build + +clean: + rm -f test-*.js + rm -f $(TESTER) + +err: $(TESTER) + for test in test-*.js; do ./$^ $$test; done 2>$@ diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/test/tester.go b/Godeps/_workspace/src/github.com/obscuren/otto/test/tester.go new file mode 100644 index 000000000..ea694fd8d --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/test/tester.go @@ -0,0 +1,196 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "regexp" + "strings" + "sync" + "text/tabwriter" + + "github.com/robertkrimen/otto" + "github.com/robertkrimen/otto/parser" +) + +var flag_test *bool = flag.Bool("test", false, "") +var flag_report *bool = flag.Bool("report", false, "") + +var match_ReferenceError_not_defined = regexp.MustCompile(`^ReferenceError: \S+ is not defined$`) +var match_lookahead = regexp.MustCompile(`Invalid regular expression: re2: Invalid \(\?[=!]\) `) +var match_backreference = regexp.MustCompile(`Invalid regular expression: re2: Invalid \\\d `) +var match_TypeError_undefined = regexp.MustCompile(`^TypeError: Cannot access member '[^']+' of undefined$`) + +var target = map[string]string{ + "test-angular-bindonce.js": "fail", // (anonymous): Line 1:944 Unexpected token ( (and 40 more errors) + "test-jsforce.js": "fail", // (anonymous): Line 9:28329 RuneError (and 5 more errors) + "test-chaplin.js": "parse", // Error: Chaplin requires Common.js or AMD modules + "test-dropbox.js.js": "parse", // Error: dropbox.js loaded in an unsupported JavaScript environment. + "test-epitome.js": "parse", // TypeError: undefined is not a function + "test-portal.js": "parse", // TypeError + "test-reactive-coffee.js": "parse", // Dependencies are not met for reactive: _ and $ not found + "test-scriptaculous.js": "parse", // script.aculo.us requires the Prototype JavaScript framework >= 1.6.0.3 + "test-waypoints.js": "parse", // TypeError: undefined is not a function + "test-webuploader.js": "parse", // Error: `jQuery` is undefined + "test-xuijs.js": "parse", // TypeError: undefined is not a function +} + +// http://cdnjs.com/ +// http://api.cdnjs.com/libraries + +func fetch(name, location string) error { + response, err := http.Get(location) + if err != nil { + return err + } + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + if !strings.HasSuffix(location, ".js") { + return nil + } + + filename := "test-" + name + ".js" + fmt.Println(filename, len(body)) + return ioutil.WriteFile(filename, body, 0644) +} + +func test(filename string) error { + script, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + if !*flag_report { + fmt.Fprintln(os.Stdout, filename, len(script)) + } + + parse := false + option := target[filename] + + if option != "parse" { + vm := otto.New() + _, err = vm.Run(string(script)) + if err != nil { + value := err.Error() + switch { + case match_ReferenceError_not_defined.MatchString(value): + case match_TypeError_undefined.MatchString(value): + case match_lookahead.MatchString(value): + case match_backreference.MatchString(value): + default: + return err + } + parse = true + } + } + + if parse { + _, err = parser.ParseFile(nil, filename, string(script), parser.IgnoreRegExpErrors) + if err != nil { + return err + } + target[filename] = "parse" + } + + return nil +} + +func main() { + flag.Parse() + + filename := "" + + err := func() error { + + if flag.Arg(0) == "fetch" { + response, err := http.Get("http://api.cdnjs.com/libraries") + if err != nil { + return err + } + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + var tmp map[string]interface{} + + err = json.Unmarshal(body, &tmp) + if err != nil { + return err + } + + var wg sync.WaitGroup + + for _, value := range tmp["results"].([]interface{}) { + wg.Add(1) + library := value.(map[string]interface{}) + go func() { + defer wg.Done() + fetch(library["name"].(string), library["latest"].(string)) + }() + } + + wg.Wait() + + return nil + } + + if *flag_report { + files, err := ioutil.ReadDir(".") + if err != nil { + return err + } + writer := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Fprintln(writer, "", "\t| Status") + fmt.Fprintln(writer, "---", "\t| ---") + for _, file := range files { + filename := file.Name() + if !strings.HasPrefix(filename, "test-") { + continue + } + err := test(filename) + option := target[filename] + name := strings.TrimPrefix(strings.TrimSuffix(filename, ".js"), "test-") + if err == nil { + switch option { + case "": + fmt.Fprintln(writer, name, "\t| pass") + case "parse": + fmt.Fprintln(writer, name, "\t| pass (parse)") + case "re2": + continue + fmt.Fprintln(writer, name, "\t| unknown (re2)") + } + } else { + fmt.Fprintln(writer, name, "\t| fail") + } + } + writer.Flush() + return nil + } + + filename = flag.Arg(0) + return test(filename) + + }() + if err != nil { + if filename != "" { + if *flag_test && target[filename] == "fail" { + goto exit + } + fmt.Fprintf(os.Stderr, "%s: %s\n", filename, err.Error()) + } else { + fmt.Fprintln(os.Stderr, err) + } + os.Exit(64) + } +exit: +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/testing_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/testing_test.go new file mode 100644 index 000000000..90d1771b9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/testing_test.go @@ -0,0 +1,128 @@ +package otto + +import ( + "./terst" + "errors" + "strings" + "testing" + "time" +) + +func tt(t *testing.T, arguments ...func()) { + halt := errors.New("A test was taking too long") + timer := time.AfterFunc(2*time.Second, func() { + panic(halt) + }) + defer func() { + timer.Stop() + }() + terst.Terst(t, arguments...) +} + +func is(arguments ...interface{}) bool { + var got, expect interface{} + + switch len(arguments) { + case 0, 1: + return terst.Is(arguments...) + case 2: + got, expect = arguments[0], arguments[1] + default: + got, expect = arguments[0], arguments[2] + } + + if value, ok := got.(Value); ok { + if _, ok := expect.(Value); !ok { + if value.value != nil { + got = value.value + } + } + } + + if len(arguments) == 2 { + arguments[0] = got + arguments[1] = expect + } else { + arguments[0] = got + arguments[2] = expect + } + + return terst.Is(arguments...) +} + +func test(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { + tester := newTester() + if len(arguments) > 0 { + tester.test(arguments[0].(string)) + } + return tester.test, tester +} + +type _tester struct { + vm *Otto +} + +func newTester() *_tester { + return &_tester{ + vm: New(), + } +} + +func (self *_tester) Get(name string) (Value, error) { + return self.vm.Get(name) +} + +func (self *_tester) Set(name string, value interface{}) Value { + err := self.vm.Set(name, value) + is(err, nil) + if err != nil { + terst.Caller().T().FailNow() + } + return self.vm.getValue(name) +} + +func (self *_tester) Run(src interface{}) (Value, error) { + return self.vm.Run(src) +} + +func (self *_tester) test(name string, expect ...interface{}) Value { + vm := self.vm + raise := false + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + if raise { + if len(expect) > 0 { + is(caught, expect[0]) + } + } else { + dbg("Panic, caught:", caught) + panic(caught) + } + } + }() + var value Value + var err error + if isIdentifier(name) { + value = vm.getValue(name) + } else { + source := name + index := strings.Index(source, "raise:") + if index == 0 { + raise = true + source = source[6:] + source = strings.TrimLeft(source, " ") + } + value, err = vm.runtime.cmpl_run(source) + if err != nil { + panic(err) + } + } + value = vm.runtime.GetValue(value) + if len(expect) > 0 { + is(value, expect[0]) + } + return value +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/token/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/token/Makefile new file mode 100644 index 000000000..1e85c7348 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/token/Makefile @@ -0,0 +1,2 @@ +token_const.go: tokenfmt + ./$^ | gofmt > $@ diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/token/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/token/README.markdown new file mode 100644 index 000000000..1865f23cc --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/token/README.markdown @@ -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 diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/token/token.go b/Godeps/_workspace/src/github.com/obscuren/otto/token/token.go new file mode 100644 index 000000000..0e941ac96 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/token/token.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/token/token_const.go b/Godeps/_workspace/src/github.com/obscuren/otto/token/token_const.go new file mode 100644 index 000000000..302153349 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/token/token_const.go @@ -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, + }, +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/token/tokenfmt b/Godeps/_workspace/src/github.com/obscuren/otto/token/tokenfmt new file mode 100644 index 000000000..63dd5d9e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/token/tokenfmt @@ -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_ +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_arguments.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_arguments.go new file mode 100644 index 000000000..e23912ace --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_arguments.go @@ -0,0 +1,106 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, environment _environment, 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, + environment: environment, + } + + 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" + // ... + environment _environment +} + +func (self0 _argumentsObject) clone(clone *_clone) _argumentsObject { + indexOfParameterName := make([]string, len(self0.indexOfParameterName)) + copy(indexOfParameterName, self0.indexOfParameterName) + return _argumentsObject{ + indexOfParameterName, + clone.environment(self0.environment), + } +} + +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.environment.GetBindingValue(name, false), true + } + return Value{}, false +} + +func (self _argumentsObject) put(name string, value Value) { + index := stringToArrayIndex(name) + name = self.indexOfParameterName[index] + self.environment.SetMutableBinding(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 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_array.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_array.go new file mode 100644 index 000000000..6c72824ff --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_array.go @@ -0,0 +1,108 @@ +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(value Value) uint32 { + tmp := toInteger(value) + if !tmp.exact() || !isUint32(tmp.value) { + panic(newRangeError()) + } + return uint32(tmp.value) +} + +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(newTypeError()) + } + newLength := arrayUint32(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 -= 1 + 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(newTypeError()) + } + return false +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_boolean.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_boolean.go new file mode 100644 index 000000000..c24409de1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_boolean.go @@ -0,0 +1,13 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newBooleanObject(value Value) *_object { + return runtime.newPrimitiveObject("Boolean", toValue_bool(toBoolean(value))) +} + +func booleanToString(value bool) string { + return strconv.FormatBool(value) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_date.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_date.go new file mode 100644 index 000000000..65e822f38 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_date.go @@ -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(_dateObject *_object) _dateObject { + if _dateObject == nil || _dateObject.class != "Date" { + panic(newTypeError()) + } + 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 := toFloat(argumentList[index]) + 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(toString(value)) + } + + return toFloat(value) + } + +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() +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_error.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_error.go new file mode 100644 index 000000000..683fcf929 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_error.go @@ -0,0 +1,9 @@ +package otto + +func (runtime *_runtime) newErrorObject(message Value) *_object { + self := runtime.newClassObject("Error") + if message.IsDefined() { + self.defineProperty("message", toValue_string(toString(message)), 0111, false) + } + return self +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_function.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_function.go new file mode 100644 index 000000000..8a6fee9d7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_function.go @@ -0,0 +1,276 @@ +package otto + +import ( + "fmt" +) + +type _functionObject struct { + call _callFunction + construct _constructFunction +} + +func (self _functionObject) source(object *_object) string { + return self.call.Source(object) +} + +func (self0 _functionObject) clone(clone *_clone) _functionObject { + return _functionObject{ + clone.callFunction(self0.call), + self0.construct, + } +} + +func (runtime *_runtime) newNativeFunctionObject(name string, native _nativeFunction, length int) *_object { + self := runtime.newClassObject("Function") + self.value = _functionObject{ + call: newNativeCallFunction(native), + construct: defaultConstructFunction, + } + self.defineProperty("length", toValue_int(length), 0000, false) + return self +} + +func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object { + self := runtime.newClassObject("Function") + self.value = _functionObject{ + call: newBoundCallFunction(target, this, argumentList), + construct: newBoundConstructFunction(target), + } + length := int(toInt32(target.get("length"))) + length -= len(argumentList) + if length < 0 { + length = 0 + } + self.defineProperty("length", toValue_int(length), 0000, false) + self.defineProperty("caller", UndefinedValue(), 0000, false) // TODO Should throw a TypeError + self.defineProperty("arguments", UndefinedValue(), 0000, false) // TODO Should throw a TypeError + return self +} + +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 +} + +func (self *_object) functionValue() _functionObject { + value, _ := self.value.(_functionObject) + return value +} + +func (self *_object) Call(this Value, argumentList ...interface{}) Value { + if self.functionValue().call == nil { + panic(newTypeError("%v is not a function", toValue_object(self))) + } + return self.runtime.Call(self, this, self.runtime.toValueArray(argumentList...), false) + // ... -> runtime -> self.Function.Call.Dispatch -> ... +} + +func (self *_object) Construct(this Value, argumentList ...interface{}) Value { + function := self.functionValue() + if function.call == nil { + panic(newTypeError("%v is not a function", toValue_object(self))) + } + if function.construct == nil { + panic(newTypeError("%v is not a constructor", toValue_object(self))) + } + return function.construct(self, this, self.runtime.toValueArray(argumentList...)) +} + +func defaultConstructFunction(self *_object, this Value, argumentList []Value) Value { + newObject := self.runtime.newObject() + newObject.class = "Object" + prototypeValue := self.get("prototype") + if !prototypeValue.IsObject() { + prototypeValue = toValue_object(self.runtime.Global.ObjectPrototype) + } + newObject.prototype = prototypeValue._object() + newObjectValue := toValue_object(newObject) + result := self.Call(newObjectValue, argumentList) + if result.IsObject() { + return result + } + return newObjectValue +} + +func (self *_object) callGet(this Value) Value { + return self.runtime.Call(self, this, []Value(nil), false) +} + +func (self *_object) callSet(this Value, value Value) { + self.runtime.Call(self, this, []Value{value}, false) +} + +// 15.3.5.3 +func (self *_object) HasInstance(of Value) bool { + if self.functionValue().call == nil { + // We should not have a HasInstance method + panic(newTypeError()) + } + if !of.IsObject() { + return false + } + prototype := self.get("prototype") + if !prototype.IsObject() { + panic(newTypeError()) + } + prototypeObject := prototype._object() + + value := of._object().prototype + for value != nil { + if value == prototypeObject { + return true + } + value = value.prototype + } + return false +} + +type _nativeFunction func(FunctionCall) Value + +// _constructFunction +type _constructFunction func(*_object, Value, []Value) Value + +// _callFunction +type _callFunction interface { + Dispatch(*_object, *_functionEnvironment, *_runtime, Value, []Value, bool) Value + Source(*_object) string + ScopeEnvironment() _environment + clone(clone *_clone) _callFunction +} + +// _nativeCallFunction +type _nativeCallFunction struct { + name string + function _nativeFunction +} + +func newNativeCallFunction(native _nativeFunction) _nativeCallFunction { + return _nativeCallFunction{"", native} +} + +func (self _nativeCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, evalHint bool) Value { + return self.function(FunctionCall{ + runtime: runtime, + evalHint: evalHint, + + This: this, + ArgumentList: argumentList, + Otto: runtime.Otto, + }) +} + +func (self _nativeCallFunction) ScopeEnvironment() _environment { + return nil +} + +func (self _nativeCallFunction) Source(*_object) string { + return fmt.Sprintf("function %s() { [native code] }", self.name) +} + +func (self0 _nativeCallFunction) clone(clone *_clone) _callFunction { + return self0 +} + +// _boundCallFunction +type _boundCallFunction struct { + target *_object + this Value + argumentList []Value +} + +func newBoundCallFunction(target *_object, this Value, argumentList []Value) *_boundCallFunction { + self := &_boundCallFunction{ + target: target, + this: this, + argumentList: argumentList, + } + return self +} + +func (self _boundCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value { + argumentList = append(self.argumentList, argumentList...) + return runtime.Call(self.target, self.this, argumentList, false) +} + +func (self _boundCallFunction) ScopeEnvironment() _environment { + return nil +} + +func (self _boundCallFunction) Source(*_object) string { + return "" +} + +func (self0 _boundCallFunction) clone(clone *_clone) _callFunction { + return _boundCallFunction{ + target: clone.object(self0.target), + this: clone.value(self0.this), + argumentList: clone.valueArray(self0.argumentList), + } +} + +func newBoundConstructFunction(target *_object) _constructFunction { + // This is not exactly as described in 15.3.4.5.2, we let [[Call]] supply the + // bound arguments, etc. + return func(self *_object, this Value, argumentList []Value) Value { + switch value := target.value.(type) { + case _functionObject: + return value.construct(self, this, argumentList) + } + panic(newTypeError()) + } +} + +// FunctionCall{} + +// FunctionCall is an encapsulation of a JavaScript function call. +type FunctionCall struct { + runtime *_runtime + _thisObject *_object + evalHint bool + + 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.runtime.GetValue(self.This) // 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(newTypeError()) + } + return self._thisObject +} + +func (self FunctionCall) toObject(value Value) *_object { + return self.runtime.toObject(value) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_go_array.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_array.go new file mode 100644 index 000000000..cf2ddd437 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_array.go @@ -0,0 +1,134 @@ +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(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.getValue(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, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + value := UndefinedValue() + reflectValue, exists := object.getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + mode: object.propertyMode, + } + } + + 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 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 typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goArrayDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return typeErrorResult(throw) + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + if object.writable { + indexValue, exists := object.getValue(index) + if exists { + indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem())) + return true + } + } + return typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_go_map.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_map.go new file mode 100644 index 000000000..4635fb231 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_map.go @@ -0,0 +1,87 @@ +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} + } + + 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(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 typeErrorResult(throw) + } + if !descriptor.isDataDescriptor() { + return 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_go_slice.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_slice.go new file mode 100644 index 000000000..5821e2ad3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_slice.go @@ -0,0 +1,118 @@ +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 := UndefinedValue() + reflectValue, exists := self.value.(*_goSliceObject).getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + 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 typeErrorResult(throw) + } else if index := stringToArrayIndex(name); index >= 0 { + if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) { + return true + } + return typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goSliceDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return 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 typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_go_struct.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_struct.go new file mode 100644 index 000000000..4abdf8075 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_go_struct.go @@ -0,0 +1,150 @@ +package otto + +import ( + "encoding/json" + "reflect" + "unicode" +) + +func upCase(str *string) { + a := []rune(*str) + a[0] = unicode.ToUpper(a[0]) + *str = string(a) +} + +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 { + upCase(&name) + + 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) field(name string) (reflect.StructField, bool) { + upCase(&name) + + return reflect.Indirect(self.value).Type().FieldByName(name) +} + +func (self _goStructObject) method(name string) (reflect.Method, bool) { + upCase(&name) + + return reflect.Indirect(self.value).Type().MethodByName(name) +} + +func (self _goStructObject) setValue(name string, value Value) bool { + field, exists := self.field(name) + if !exists { + return false + } + fieldValue := self.getValue(name) + reflectValue, err := value.toReflectValue(field.Type.Kind()) + if err != nil { + panic(err) + } + fieldValue.Set(reflectValue) + 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(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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_number.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_number.go new file mode 100644 index 000000000..ee7adc985 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_number.go @@ -0,0 +1,5 @@ +package otto + +func (runtime *_runtime) newNumberObject(value Value) *_object { + return runtime.newPrimitiveObject("Number", toNumber(value)) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_reference.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_reference.go new file mode 100644 index 000000000..6c4f37278 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_reference.go @@ -0,0 +1,157 @@ +package otto + +import ( + "github.com/robertkrimen/otto/ast" +) + +type _reference interface { + GetBase() interface{} // GetBase + GetName() string // GetReferencedName + IsStrict() bool // IsStrictReference + IsUnresolvable() bool // IsUnresolvableReference + IsPropertyReference() bool // IsPropertyReference + GetValue() Value // GetValue + PutValue(Value) bool // PutValue + Delete() bool +} + +// Reference + +type _referenceDefault struct { + name string + strict bool +} + +func (self _referenceDefault) GetName() string { + return self.name +} + +func (self _referenceDefault) IsStrict() bool { + return self.strict +} + +// PropertyReference + +type _propertyReference struct { + _referenceDefault + Base *_object +} + +func newPropertyReference(base *_object, name string, strict bool) *_propertyReference { + return &_propertyReference{ + Base: base, + _referenceDefault: _referenceDefault{ + name: name, + strict: strict, + }, + } +} + +func (self *_propertyReference) GetBase() interface{} { + return self.Base +} + +func (self *_propertyReference) IsUnresolvable() bool { + return self.Base == nil +} + +func (self *_propertyReference) IsPropertyReference() bool { + return true +} + +func (self *_propertyReference) GetValue() Value { + if self.Base == nil { + panic(newReferenceError("notDefined", self.name)) + } + return self.Base.get(self.name) +} + +func (self *_propertyReference) PutValue(value Value) bool { + if self.Base == nil { + return false + } + self.Base.put(self.name, value, self.IsStrict()) + return true +} + +func (self *_propertyReference) Delete() bool { + if self.Base == nil { + // TODO Throw an error if strict + return true + } + return self.Base.delete(self.name, self.IsStrict()) +} + +// ArgumentReference + +func newArgumentReference(base *_object, name string, strict bool) *_propertyReference { + if base == nil { + panic(hereBeDragons()) + } + return newPropertyReference(base, name, strict) +} + +type _environmentReference struct { + _referenceDefault + Base _environment + node ast.Node +} + +func newEnvironmentReference(base _environment, name string, strict bool, node ast.Node) *_environmentReference { + return &_environmentReference{ + Base: base, + _referenceDefault: _referenceDefault{ + name: name, + strict: strict, + }, + node: node, + } +} + +func (self *_environmentReference) GetBase() interface{} { + return self.Base +} + +func (self *_environmentReference) IsUnresolvable() bool { + return self.Base == nil // The base (an environment) will never be nil +} + +func (self *_environmentReference) IsPropertyReference() bool { + return false +} + +func (self *_environmentReference) GetValue() Value { + if self.Base == nil { + // This should never be reached, but just in case + } + return self.Base.GetValue(self.name, self.IsStrict()) +} + +func (self *_environmentReference) PutValue(value Value) bool { + if self.Base == nil { + // This should never be reached, but just in case + return false + } + self.Base.SetValue(self.name, value, self.IsStrict()) + return true +} + +func (self *_environmentReference) 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(environment _environment, name string, strict bool) _reference { + if environment == nil { + return newPropertyReference(nil, name, strict) + } + if environment.HasBinding(name) { + return environment.newReference(name, strict) + } + return getIdentifierReference(environment.Outer(), name, strict) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_regexp.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_regexp.go new file mode 100644 index 000000000..5f17bde69 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_regexp.go @@ -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(newError("SyntaxError: newRegExpObject: %s %s", pattern, flags)) + } + global = true + case 'm': + if multiline { + panic(newError("SyntaxError: newRegExpObject: %s %s", pattern, flags)) + } + multiline = true + re2flags += "m" + case 'i': + if ignoreCase { + panic(newError("SyntaxError: newRegExpObject: %s %s", pattern, flags)) + } + ignoreCase = true + re2flags += "i" + } + } + + re2pattern, err := parser.TransformRegExp(pattern) + if err != nil { + panic(newTypeError("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(newSyntaxError("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(newTypeError("Calling RegExp.exec on a non-RegExp object")) + } + lastIndex := toInteger(this.get("lastIndex")).value + index := lastIndex + global := toBoolean(this.get("global")) + 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] = UndefinedValue() + } + } + 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/type_string.go b/Godeps/_workspace/src/github.com/obscuren/otto/type_string.go new file mode 100644 index 000000000..fd93316d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/type_string.go @@ -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(toString(value)) + + 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 +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore/Makefile b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/Makefile new file mode 100644 index 000000000..fc872917f --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/Makefile @@ -0,0 +1,11 @@ +.PHONY: source + +source: source.go + +underscore.js: + curl -kL http://underscorejs.org/underscore.js > $@ + +source.go: underscore.js + go-bindata -f underscore -p underscore -u true < $< 2>/dev/null | grep -v '^//' | gofmt > $@ + head -4 $< >> $@ + mv $< .. diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore/README.markdown b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/README.markdown new file mode 100644 index 000000000..bce37b695 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/README.markdown @@ -0,0 +1,53 @@ +# underscore +-- + import "github.com/robertkrimen/otto/underscore" + +Package underscore contains the source for the JavaScript utility-belt library. + + import ( + _ "github.com/robertkrimen/otto/underscore" + ) + // Every Otto runtime will now include underscore + +http://underscorejs.org + +https://github.com/documentcloud/underscore + +By importing this package, you'll automatically load underscore every time you +create a new Otto runtime. + +To prevent this behavior, you can do the following: + + import ( + "github.com/robertkrimen/otto/underscore" + ) + + func init() { + underscore.Disable() + } + +## Usage + +#### func Disable + +```go +func Disable() +``` +Disable underscore runtime inclusion. + +#### func Enable + +```go +func Enable() +``` +Enable underscore runtime inclusion. + +#### func Source + +```go +func Source() string +``` +Source returns the underscore source. + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore/source.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/source.go new file mode 100644 index 000000000..65754ab71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/source.go @@ -0,0 +1,3462 @@ +package underscore + +func underscore() []byte { + return []byte{ + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x31, 0x2e, 0x34, + 0x2e, 0x34, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x6a, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, + 0x39, 0x2d, 0x32, 0x30, 0x31, 0x33, 0x20, 0x4a, 0x65, 0x72, 0x65, 0x6d, + 0x79, 0x20, 0x41, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x61, 0x73, 0x2c, 0x20, + 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, + 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x66, 0x72, 0x65, 0x65, + 0x6c, 0x79, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x4d, 0x49, 0x54, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x2e, 0x0a, 0x0a, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, + 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x73, 0x65, 0x74, 0x75, + 0x70, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x60, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x60, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, + 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x60, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x60, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x3d, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x5f, 0x60, 0x20, 0x76, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, + 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x3d, 0x20, + 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x67, 0x65, 0x74, 0x73, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x76, 0x65, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6d, 0x69, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x28, 0x62, 0x75, + 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x7a, 0x69, 0x70, 0x70, 0x65, + 0x64, 0x29, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, + 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x70, 0x65, 0x65, + 0x64, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x63, 0x6f, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, + 0x75, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, + 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x4f, + 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x68, 0x61, 0x73, 0x4f, + 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x2a, 0x2a, + 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, + 0x2a, 0x2a, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x65, 0x64, + 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, + 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x61, + 0x70, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, + 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x3d, 0x20, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x76, 0x65, 0x72, + 0x79, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x4f, 0x66, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, + 0x69, 0x6e, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x62, 0x69, 0x6e, 0x64, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x61, 0x66, + 0x65, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x62, 0x65, 0x6c, + 0x6f, 0x77, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x5f, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x5f, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, + 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x4e, 0x6f, 0x64, 0x65, + 0x2e, 0x6a, 0x73, 0x2a, 0x2a, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, + 0x64, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x60, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x28, 0x29, 0x60, 0x20, 0x41, 0x50, 0x49, 0x2e, 0x20, 0x49, 0x66, + 0x20, 0x77, 0x65, 0x27, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, + 0x65, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x20, 0x60, 0x5f, 0x60, 0x20, + 0x61, 0x73, 0x20, 0x61, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, + 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x22, 0x61, + 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x20, 0x6d, 0x6f, 0x64, + 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, + 0x65, 0x6f, 0x66, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, + 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x27, 0x20, 0x26, 0x26, 0x20, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, + 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2e, + 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x6f, + 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, + 0x20, 0x3d, 0x20, 0x27, 0x31, 0x2e, 0x34, 0x2e, 0x34, 0x27, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x73, 0x74, 0x6f, + 0x6e, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x65, 0x61, 0x63, 0x68, + 0x60, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x61, 0x6b, 0x61, 0x20, 0x60, 0x66, + 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x2d, 0x69, 0x6e, 0x20, + 0x60, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2c, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, + 0x61, 0x77, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x6f, 0x72, + 0x45, 0x61, 0x63, 0x68, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x65, + 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x72, 0x45, + 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, + 0x45, 0x61, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, + 0x68, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, + 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, + 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, + 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x65, 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, + 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, + 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x6d, + 0x61, 0x70, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x70, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, + 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, + 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x20, 0x3d, 0x20, 0x27, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2a, 0x2a, 0x52, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x2a, 0x2a, 0x20, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x73, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6b, 0x61, + 0x20, 0x60, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x2c, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, + 0x6c, 0x60, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, 0x64, 0x75, + 0x63, 0x65, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, + 0x64, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, 0x62, + 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, + 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3f, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x28, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x29, 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, + 0x63, 0x65, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, + 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x2c, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x20, 0x61, 0x73, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, 0x72, 0x60, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, + 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, + 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x60, 0x20, 0x69, + 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, + 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, + 0x64, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, + 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, + 0x62, 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x26, + 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, + 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, + 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x29, 0x20, 0x3a, + 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, + 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x2b, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, + 0x3d, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, + 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3f, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x5b, + 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3a, 0x20, + 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x6f, + 0x62, 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, + 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, + 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, + 0x68, 0x69, 0x63, 0x68, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x20, + 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x60, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x70, 0x61, 0x73, 0x73, 0x20, 0x61, 0x20, 0x74, + 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, + 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x60, 0x65, 0x76, 0x65, 0x72, 0x79, 0x60, 0x20, 0x69, 0x66, + 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6c, 0x6c, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x5f, + 0x2e, 0x61, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, + 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x65, + 0x76, 0x65, 0x72, 0x79, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x26, 0x26, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, + 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x60, 0x73, 0x6f, 0x6d, 0x65, 0x60, 0x20, 0x69, + 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, + 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6e, 0x79, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x3d, 0x20, + 0x5f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x61, + 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, + 0x6f, 0x6d, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x28, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x7c, 0x7c, + 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x3d, 0x3d, 0x3d, 0x60, + 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x2d, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, + 0x20, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x28, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, + 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, + 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3f, 0x20, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x29, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, + 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x6d, 0x61, + 0x70, 0x60, 0x3a, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, + 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x60, 0x3a, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, + 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x20, 0x3a, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5b, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x27, 0x66, 0x69, 0x6e, 0x64, + 0x27, 0x20, 0x3a, 0x20, 0x27, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x27, + 0x5d, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x61, + 0x74, 0x74, 0x72, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x74, 0x74, 0x72, + 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6e, 0x64, + 0x60, 0x3a, 0x20, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x57, 0x68, 0x65, 0x72, + 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x61, 0x6e, 0x27, + 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x65, 0x72, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x36, 0x35, 0x2c, 0x35, 0x33, 0x35, + 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x3a, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x62, 0x75, 0x67, 0x73, 0x2e, 0x77, 0x65, + 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x73, 0x68, 0x6f, + 0x77, 0x5f, 0x62, 0x75, 0x67, 0x2e, 0x63, 0x67, 0x69, 0x3f, 0x69, 0x64, + 0x3d, 0x38, 0x30, 0x37, 0x39, 0x37, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, + 0x61, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, + 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, + 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x49, 0x6e, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, + 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, + 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3d, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, + 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, + 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3e, 0x3d, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, + 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x6f, 0x72, 0x20, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, + 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, + 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, + 0x74, 0x68, 0x2e, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x49, + 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x3f, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3c, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x72, 0x61, 0x6e, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x68, 0x75, + 0x66, 0x66, 0x6c, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2b, + 0x2b, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, + 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x68, 0x75, 0x66, + 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x5b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x3b, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, + 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, + 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, + 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, + 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, + 0x6b, 0x28, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x20, 0x3a, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x65, 0x66, 0x74, 0x2c, 0x20, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x6c, 0x65, + 0x66, 0x74, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, + 0x20, 0x3d, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x62, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x20, 0x62, 0x20, 0x7c, 0x7c, + 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x30, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x61, 0x20, 0x3c, 0x20, 0x62, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x65, 0x66, 0x74, + 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3c, 0x20, 0x72, 0x69, 0x67, + 0x68, 0x74, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, 0x20, 0x2d, + 0x31, 0x20, 0x3a, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x29, 0x2c, 0x20, 0x27, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x27, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x20, 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, + 0x79, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x62, + 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x62, + 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, + 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, + 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, + 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, + 0x3d, 0x20, 0x5b, 0x5d, 0x29, 0x29, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x20, 0x61, + 0x20, 0x63, 0x65, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x6f, + 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, + 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, + 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, + 0x5d, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x55, 0x73, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x20, + 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x6c, + 0x6c, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x61, + 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, + 0x65, 0x72, 0x74, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x20, 0x61, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x73, 0x20, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3f, + 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, + 0x3a, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x77, + 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x3d, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, + 0x20, 0x28, 0x6c, 0x6f, 0x77, 0x20, 0x3c, 0x20, 0x68, 0x69, 0x67, 0x68, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6d, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x28, 0x6c, 0x6f, 0x77, + 0x20, 0x2b, 0x20, 0x68, 0x69, 0x67, 0x68, 0x29, 0x20, 0x3e, 0x3e, 0x3e, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x5b, 0x6d, 0x69, 0x64, 0x5d, 0x29, 0x20, 0x3c, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3f, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x3d, + 0x20, 0x6d, 0x69, 0x64, 0x20, 0x2b, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x68, + 0x69, 0x67, 0x68, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x66, 0x65, + 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x61, + 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, + 0x72, 0x65, 0x61, 0x6c, 0x2c, 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x6f, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, + 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3a, 0x20, 0x5f, 0x2e, + 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x50, + 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, + 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x4e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x60, 0x68, 0x65, 0x61, 0x64, 0x60, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x74, 0x61, 0x6b, 0x65, 0x60, 0x2e, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, + 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, + 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x20, 0x3f, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x30, + 0x2c, 0x20, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, + 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, + 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x45, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x20, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, + 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, + 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x65, 0x78, 0x63, 0x6c, + 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, + 0x73, 0x74, 0x20, 0x4e, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, + 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, + 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x6e, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7c, 0x7c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x3a, 0x20, + 0x6e, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6c, 0x61, 0x73, 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, + 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, + 0x20, 0x4e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, + 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6e, + 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, + 0x20, 0x21, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, + 0x61, 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x6e, 0x2c, 0x20, 0x30, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, + 0x20, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x60, 0x74, 0x61, 0x69, 0x6c, 0x60, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x64, 0x72, 0x6f, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x45, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, + 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, + 0x65, 0x73, 0x74, 0x20, 0x4e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x2a, 0x2a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x72, + 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x28, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, + 0x20, 0x31, 0x20, 0x3a, 0x20, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x72, 0x69, 0x6d, + 0x20, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x61, 0x6c, + 0x73, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, + 0x76, 0x65, 0x20, 0x60, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x60, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x3f, 0x20, + 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x20, 0x3a, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x6c, + 0x79, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x20, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2c, + 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x20, + 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2d, 0x66, 0x72, + 0x65, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, + 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x20, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x60, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, 0x5f, 0x2e, + 0x6d, 0x61, 0x70, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x65, + 0x6e, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3f, 0x20, + 0x28, 0x21, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x7c, 0x7c, 0x20, 0x73, + 0x65, 0x65, 0x6e, 0x5b, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x21, 0x3d, 0x3d, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x21, 0x5f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x73, 0x65, + 0x65, 0x6e, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, + 0x65, 0x6e, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, + 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x65, 0x61, 0x63, + 0x68, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x20, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, + 0x75, 0x6e, 0x69, 0x71, 0x28, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, + 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, + 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x65, + 0x76, 0x65, 0x72, 0x79, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x61, 0x6b, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x61, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, + 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, + 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x77, 0x69, 0x6c, + 0x6c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, + 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x5f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x5a, 0x69, 0x70, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, + 0x2d, 0x2d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x67, 0x6f, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x7a, 0x69, 0x70, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, + 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x78, 0x28, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, 0x72, + 0x67, 0x73, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x27, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, + 0x72, 0x67, 0x73, 0x2c, 0x20, 0x22, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x73, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5d, 0x60, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, + 0x69, 0x72, 0x73, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x20, + 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, + 0x2d, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x79, + 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, + 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, + 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, 0x69, 0x73, 0x74, 0x5b, + 0x69, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, + 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x30, 0x5d, 0x5d, 0x20, 0x3d, + 0x20, 0x6c, 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x31, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x20, + 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, + 0x6c, 0x79, 0x20, 0x75, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x28, 0x49, 0x27, 0x6d, 0x20, + 0x6c, 0x6f, 0x6f, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x20, 0x79, + 0x6f, 0x75, 0x2c, 0x20, 0x2a, 0x2a, 0x4d, 0x53, 0x49, 0x45, 0x2a, 0x2a, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, 0x65, 0x20, 0x6e, + 0x65, 0x65, 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, + 0x31, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x74, 0x65, + 0x6d, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x60, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, + 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x69, 0x73, + 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, + 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, + 0x72, 0x74, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x20, 0x60, 0x74, 0x72, 0x75, 0x65, 0x60, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x69, 0x73, 0x53, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2a, 0x2a, 0x20, 0x74, 0x6f, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x53, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, + 0x66, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, + 0x3d, 0x20, 0x27, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, + 0x3c, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, + 0x61, 0x78, 0x28, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x2b, 0x20, 0x69, 0x73, + 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, + 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, + 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, + 0x20, 0x3f, 0x20, 0x69, 0x20, 0x3a, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, + 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, + 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x60, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6c, 0x61, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2c, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x28, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x20, 0x3f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x3a, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, + 0x28, 0x69, 0x2d, 0x2d, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x69, + 0x74, 0x68, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x41, 0x20, 0x70, + 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x50, + 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x60, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x28, 0x29, 0x60, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x20, 0x53, 0x65, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x5b, + 0x74, 0x68, 0x65, 0x20, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x64, + 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5d, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, + 0x73, 0x2e, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2e, 0x6f, 0x72, 0x67, + 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2f, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x23, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, + 0x73, 0x74, 0x6f, 0x70, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x7c, 0x7c, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x7c, + 0x7c, 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, + 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x63, 0x65, + 0x69, 0x6c, 0x28, 0x28, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x2d, 0x20, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x29, 0x20, 0x2f, 0x20, 0x73, 0x74, 0x65, 0x70, + 0x29, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x69, 0x64, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x64, 0x78, 0x20, 0x3c, + 0x20, 0x6c, 0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5b, 0x69, 0x64, 0x78, 0x2b, + 0x2b, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, + 0x2b, 0x3d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x68, 0x65, 0x6d, 0x29, + 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x67, 0x69, + 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x28, + 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, + 0x68, 0x69, 0x73, 0x60, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, + 0x29, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x60, 0x20, 0x69, 0x66, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, + 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x20, 0x26, 0x26, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x42, 0x69, 0x6e, 0x64, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2e, 0x63, 0x6f, + 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x68, 0x61, + 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x74, + 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x66, 0x69, 0x6c, + 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, + 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, + 0x73, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x20, 0x60, 0x74, + 0x68, 0x69, 0x73, 0x60, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, + 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x42, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x27, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x20, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x62, 0x65, 0x6c, 0x6f, + 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x20, 0x7b, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x66, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, + 0x64, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x66, 0x5d, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4d, + 0x65, 0x6d, 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, + 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x65, 0x6d, + 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x7b, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6d, 0x65, + 0x6d, 0x6f, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, + 0x6d, 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, + 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, + 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, + 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x2c, 0x20, + 0x77, 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x65, 0x72, 0x73, + 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, + 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x20, 0x68, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x64, 0x65, 0x66, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x2c, + 0x20, 0x31, 0x5d, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, + 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x76, + 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, + 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x64, + 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x68, + 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, + 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, + 0x20, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x69, 0x74, 0x20, 0x2d, 0x20, + 0x28, 0x6e, 0x6f, 0x77, 0x20, 0x2d, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, + 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x6f, + 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x61, 0x73, 0x20, 0x69, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, + 0x6e, 0x75, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x6e, 0x6f, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, + 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x2e, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x64, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x74, + 0x20, 0x73, 0x74, 0x6f, 0x70, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x20, 0x49, 0x66, 0x20, + 0x60, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x60, 0x20, + 0x69, 0x73, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2c, 0x20, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6c, 0x65, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x65, 0x64, 0x67, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x62, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, + 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x2c, 0x20, 0x69, + 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, + 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x69, 0x6d, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x26, 0x26, 0x20, 0x21, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x77, + 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x63, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x29, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, + 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x6d, + 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x20, 0x68, 0x6f, 0x77, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x66, + 0x74, 0x65, 0x6e, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6c, 0x6c, + 0x20, 0x69, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, + 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x61, 0x6e, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, + 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, + 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, + 0x67, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x6a, + 0x75, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x2c, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, + 0x2c, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, + 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, + 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x65, 0x61, 0x63, 0x68, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, + 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x20, + 0x69, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x2d, 0x2d, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x73, + 0x5b, 0x69, 0x5d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, + 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x61, + 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x4e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x2d, + 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x20, 0x31, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, + 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x20, 0x60, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, + 0x65, 0x79, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, + 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, + 0x79, 0x73, 0x20, 0x7c, 0x7c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, + 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x27, + 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, + 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x6b, + 0x65, 0x79, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, + 0x65, 0x79, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, + 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, + 0x29, 0x29, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x70, 0x75, + 0x73, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x69, 0x72, 0x73, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x5b, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, + 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6f, 0x62, 0x6a, 0x5b, + 0x6b, 0x65, 0x79, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x61, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x29, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6b, 0x65, 0x79, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x20, + 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, + 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x69, + 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, 0x20, + 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, + 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6b, 0x65, 0x79, 0x73, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, + 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, + 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, + 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, + 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x61, 0x63, 0x6b, + 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, + 0x6d, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, + 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, + 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, + 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x21, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, + 0x28, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, + 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x69, 0x6c, + 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, + 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, + 0x72, 0x6f, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x70, + 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, + 0x3d, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, + 0x70, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x28, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2d, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x29, 0x20, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, + 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x5f, + 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x7b, 0x7d, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x22, + 0x74, 0x61, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x22, 0x20, 0x61, 0x20, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x2c, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, + 0x72, 0x6d, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, + 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x74, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x6f, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x63, + 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x60, 0x69, 0x73, 0x45, 0x71, + 0x75, 0x61, 0x6c, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x65, 0x71, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, + 0x2e, 0x20, 0x60, 0x30, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2d, 0x30, 0x60, + 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, + 0x72, 0x65, 0x6e, 0x27, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x63, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x61, 0x72, 0x6d, + 0x6f, 0x6e, 0x79, 0x20, 0x60, 0x65, 0x67, 0x61, 0x6c, 0x60, 0x20, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x3a, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x69, 0x6b, 0x69, 0x2e, 0x65, 0x63, 0x6d, + 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, + 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x70, 0x68, 0x70, 0x3f, 0x69, 0x64, 0x3d, + 0x68, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x79, 0x3a, 0x65, 0x67, 0x61, 0x6c, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x7c, 0x7c, + 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, + 0x2f, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x41, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6e, + 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x20, 0x62, 0x65, 0x63, + 0x61, 0x75, 0x73, 0x65, 0x20, 0x60, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, + 0x3d, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x60, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x77, + 0x72, 0x61, 0x70, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, + 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x62, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, + 0x66, 0x20, 0x5f, 0x29, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x2e, 0x5f, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, + 0x60, 0x5b, 0x5b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x5d, 0x5d, 0x60, 0x20, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, + 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, + 0x6d, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x62, 0x29, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, + 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x69, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, + 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x3b, + 0x20, 0x74, 0x68, 0x75, 0x73, 0x2c, 0x20, 0x60, 0x22, 0x35, 0x22, 0x60, + 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x60, 0x6e, 0x65, 0x77, 0x20, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x28, 0x22, 0x35, 0x22, 0x29, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5d, 0x27, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, + 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, + 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x66, 0x6c, 0x65, + 0x78, 0x69, 0x76, 0x65, 0x2e, 0x20, 0x41, 0x6e, 0x20, 0x60, 0x65, 0x67, + 0x61, 0x6c, 0x60, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, + 0x20, 0x2b, 0x61, 0x20, 0x3f, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x2b, + 0x62, 0x20, 0x3a, 0x20, 0x28, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, + 0x3f, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, + 0x20, 0x2f, 0x20, 0x62, 0x20, 0x3a, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, + 0x2b, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x44, 0x61, 0x74, 0x65, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x65, 0x72, 0x63, 0x65, 0x20, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, + 0x65, 0x61, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6d, 0x65, + 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x20, 0x44, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, + 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, + 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x72, + 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, + 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2b, 0x61, + 0x20, 0x3d, 0x3d, 0x20, 0x2b, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x73, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x52, 0x65, + 0x67, 0x45, 0x78, 0x70, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, + 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x3d, + 0x3d, 0x20, 0x62, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x26, + 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x69, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x2e, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, + 0x73, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x43, 0x61, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x20, + 0x65, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x20, + 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x64, 0x61, 0x70, 0x74, 0x65, 0x64, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x45, 0x53, 0x20, 0x35, 0x2e, 0x31, 0x20, + 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x31, + 0x32, 0x2e, 0x33, 0x2c, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x60, 0x4a, 0x4f, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x61, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, + 0x28, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x2d, 0x2d, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, 0x69, + 0x6e, 0x65, 0x61, 0x72, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, + 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x6c, + 0x79, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, + 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x5b, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x61, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x5b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, + 0x3d, 0x3d, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x76, + 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, + 0x6b, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x75, + 0x73, 0x68, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x2c, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x5d, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, + 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, + 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x3d, + 0x20, 0x62, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x67, 0x6e, + 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, + 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, + 0x65, 0x2d, 0x2d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x65, 0x71, 0x28, 0x61, + 0x5b, 0x73, 0x69, 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x73, 0x69, + 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, + 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x64, + 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, + 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x60, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x64, 0x69, + 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x61, 0x6d, + 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, + 0x3d, 0x20, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x62, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x3d, 0x20, + 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x21, 0x28, 0x5f, + 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x61, + 0x43, 0x74, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x6f, 0x66, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, + 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, + 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x62, 0x43, + 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x62, 0x43, 0x74, + 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, + 0x66, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x29, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, + 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x61, + 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, + 0x65, 0x79, 0x29, 0x20, 0x26, 0x26, 0x20, 0x65, 0x71, 0x28, 0x61, 0x5b, + 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x6b, 0x65, 0x79, 0x5d, + 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, + 0x6e, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, + 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x26, + 0x26, 0x20, 0x21, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x2d, 0x29, 0x29, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x21, + 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, + 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, + 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x65, + 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, 0x70, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, + 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x69, 0x66, 0x20, + 0x74, 0x77, 0x6f, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, + 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x71, 0x28, 0x61, 0x2c, 0x20, 0x62, + 0x2c, 0x20, 0x5b, 0x5d, 0x2c, 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, + 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, + 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x65, 0x6d, + 0x70, 0x74, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, + 0x20, 0x22, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x65, + 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x77, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x73, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, + 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, + 0x20, 0x44, 0x4f, 0x4d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, + 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6e, + 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x45, 0x43, 0x4d, 0x41, 0x35, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x7c, 0x7c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, + 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3f, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x69, 0x73, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x73, 0x3a, 0x20, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x2c, 0x20, 0x69, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2c, + 0x20, 0x69, 0x73, 0x44, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x69, 0x73, 0x52, + 0x65, 0x67, 0x45, 0x78, 0x70, 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, + 0x68, 0x28, 0x5b, 0x27, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x27, 0x2c, 0x20, 0x27, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, + 0x2c, 0x20, 0x27, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x2c, 0x20, + 0x27, 0x44, 0x61, 0x74, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x52, 0x65, 0x67, + 0x45, 0x78, 0x70, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x27, 0x69, 0x73, 0x27, 0x20, 0x2b, + 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x27, 0x20, 0x2b, + 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x61, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, + 0x69, 0x6e, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x73, 0x20, + 0x28, 0x61, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x49, 0x45, 0x29, 0x2c, 0x20, + 0x77, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x61, + 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x22, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, + 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x27, 0x63, 0x61, 0x6c, 0x6c, 0x65, + 0x65, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, + 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x60, 0x69, 0x73, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x60, 0x20, 0x69, 0x66, 0x20, + 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, + 0x66, 0x20, 0x28, 0x2f, 0x2e, 0x2f, 0x29, 0x20, 0x21, 0x3d, 0x3d, 0x20, + 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, + 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, + 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3f, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x69, 0x73, 0x4e, + 0x61, 0x4e, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x46, 0x6c, 0x6f, 0x61, + 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x3f, 0x20, 0x28, 0x4e, + 0x61, 0x4e, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, + 0x69, 0x63, 0x68, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, + 0x66, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x61, + 0x4e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, + 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, 0x20, 0x2b, 0x6f, 0x62, + 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, + 0x6c, 0x65, 0x61, 0x6e, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, + 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x6f, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, + 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, + 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x3f, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x66, 0x20, 0x61, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x79, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x73, + 0x65, 0x6c, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, + 0x79, 0x70, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x6b, 0x65, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x69, + 0x6e, 0x20, 0x2a, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, + 0x74, 0x2a, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, + 0x5f, 0x60, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x61, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, + 0x6c, 0x69, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x65, + 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2a, + 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6e, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, + 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x75, + 0x6d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, + 0x72, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6d, 0x69, + 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x28, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x29, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x69, 0x6e, + 0x2c, 0x20, 0x6d, 0x61, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x69, 0x6e, + 0x20, 0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, + 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x2d, + 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, + 0x61, 0x70, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x26, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x61, 0x6d, + 0x70, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, + 0x3c, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x6c, 0x74, 0x3b, 0x27, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x3e, 0x27, 0x3a, 0x20, 0x27, + 0x26, 0x67, 0x74, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x27, 0x22, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x71, 0x75, 0x6f, 0x74, + 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, + 0x22, 0x3a, 0x20, 0x27, 0x26, 0x23, 0x78, 0x32, 0x37, 0x3b, 0x27, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x2f, 0x27, 0x3a, 0x20, + 0x27, 0x26, 0x23, 0x78, 0x32, 0x46, 0x3b, 0x27, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, + 0x70, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, + 0x6c, 0x79, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x2e, 0x0a, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x20, 0x20, + 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, + 0x5b, 0x27, 0x20, 0x2b, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, + 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x2c, 0x20, 0x27, 0x67, + 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, + 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, 0x28, 0x27, 0x20, 0x2b, 0x20, 0x5f, + 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, + 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, + 0x2b, 0x20, 0x27, 0x29, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x2f, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, + 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, + 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x27, 0x27, 0x20, 0x2b, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x2e, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x65, 0x73, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x5b, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x5b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x6e, + 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, + 0x73, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, + 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5b, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x29, 0x20, + 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, + 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x78, 0x69, + 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x5f, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, + 0x29, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x5b, 0x6e, 0x61, + 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6e, 0x61, + 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, + 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x20, 0x3d, 0x20, 0x5b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x69, 0x64, + 0x20, 0x28, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x69, + 0x72, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x44, 0x4f, + 0x4d, 0x20, 0x69, 0x64, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, + 0x75, 0x65, 0x49, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x64, + 0x20, 0x3d, 0x20, 0x2b, 0x2b, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x20, 0x2b, 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x20, 0x3f, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, + 0x2b, 0x20, 0x69, 0x64, 0x20, 0x3a, 0x20, 0x69, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x79, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x55, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x73, 0x20, 0x45, 0x52, 0x42, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x64, 0x65, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, + 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x28, 0x5b, + 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, 0x2f, 0x67, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x3d, + 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, + 0x2f, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, + 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, + 0x25, 0x2d, 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, + 0x25, 0x3e, 0x2f, 0x67, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x60, 0x2c, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x6e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x67, 0x65, 0x78, + 0x2c, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, + 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x6f, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x2f, 0x28, 0x2e, 0x29, + 0x5e, 0x2f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x73, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, + 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, + 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x70, 0x75, 0x74, 0x20, + 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x27, 0x22, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x27, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, + 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x72, 0x27, 0x3a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x72, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x27, 0x5c, 0x6e, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x6e, + 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x74, 0x27, 0x3a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x3a, 0x20, + 0x27, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x3a, 0x20, 0x27, + 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, + 0x65, 0x72, 0x20, 0x3d, 0x20, 0x2f, 0x5c, 0x5c, 0x7c, 0x27, 0x7c, 0x5c, + 0x72, 0x7c, 0x5c, 0x6e, 0x7c, 0x5c, 0x74, 0x7c, 0x5c, 0x75, 0x32, 0x30, + 0x32, 0x38, 0x7c, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x2f, 0x67, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4a, 0x61, 0x76, 0x61, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2d, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x20, + 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x4a, + 0x6f, 0x68, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x69, 0x67, 0x27, 0x73, 0x20, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, + 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, + 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x73, 0x20, 0x77, + 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2c, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x72, 0x72, + 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, + 0x73, 0x20, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x28, 0x7b, 0x7d, 0x2c, 0x20, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x20, + 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x76, + 0x69, 0x61, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x5b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x7c, 0x7c, + 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, + 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x6f, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, + 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x7c, 0x24, 0x27, + 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x20, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x73, 0x20, 0x61, 0x70, + 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, + 0x20, 0x22, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x2c, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2c, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, + 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x2c, 0x20, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, + 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, + 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x65, 0x72, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x20, + 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x73, 0x5b, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, + 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, 0x74, 0x3d, + 0x28, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, + 0x2b, 0x20, 0x22, 0x29, 0x29, 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, + 0x27, 0x27, 0x3a, 0x5f, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, + 0x5f, 0x5f, 0x74, 0x29, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, + 0x74, 0x3d, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x29, 0x29, + 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x27, 0x27, 0x3a, 0x5f, 0x5f, + 0x74, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, + 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x5c, 0x6e, 0x5f, 0x5f, 0x70, 0x2b, + 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x3d, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x2b, 0x20, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, 0x5c, + 0x6e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x66, 0x20, 0x61, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x21, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x27, 0x77, 0x69, 0x74, 0x68, + 0x28, 0x6f, 0x62, 0x6a, 0x7c, 0x7c, 0x7b, 0x7d, 0x29, 0x7b, 0x5c, 0x6e, + 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, + 0x20, 0x27, 0x7d, 0x5c, 0x6e, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x76, + 0x61, 0x72, 0x20, 0x5f, 0x5f, 0x74, 0x2c, 0x5f, 0x5f, 0x70, 0x3d, 0x27, + 0x27, 0x2c, 0x5f, 0x5f, 0x6a, 0x3d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x6a, 0x6f, + 0x69, 0x6e, 0x2c, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, + 0x5f, 0x5f, 0x6a, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x27, 0x27, 0x29, 0x3b, 0x7d, + 0x3b, 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5f, 0x70, 0x3b, 0x5c, 0x6e, + 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x2c, 0x20, 0x27, 0x5f, + 0x27, 0x2c, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, + 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x64, 0x61, 0x74, + 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x20, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x27, 0x20, 0x2b, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x29, 0x20, 0x2b, 0x20, + 0x27, 0x29, 0x7b, 0x5c, 0x6e, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x7d, 0x27, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, + 0x61, 0x20, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x4f, 0x4f, 0x50, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x55, + 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, + 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x4f, 0x4f, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x20, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, + 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, + 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3f, 0x20, 0x5f, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, 0x29, + 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, + 0x78, 0x69, 0x6e, 0x28, 0x5f, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x75, + 0x74, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x70, + 0x6f, 0x70, 0x27, 0x2c, 0x20, 0x27, 0x70, 0x75, 0x73, 0x68, 0x27, 0x2c, + 0x20, 0x27, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x27, 0x2c, 0x20, + 0x27, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x6f, + 0x72, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, + 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, + 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, + 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, + 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, + 0x68, 0x69, 0x66, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, + 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, + 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, + 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x63, 0x6f, 0x6e, + 0x63, 0x61, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x6a, 0x6f, 0x69, 0x6e, 0x27, + 0x2c, 0x20, 0x27, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x27, 0x5d, 0x2c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, 0x6e, 0x61, 0x6d, + 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, 0x61, 0x6d, 0x65, + 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, + 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x0a, 0x7d, 0x29, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x29, 0x3b, 0x0a, + } +} +// Underscore.js 1.4.4 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore/testify b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/testify new file mode 100644 index 000000000..7f6e0f7c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/testify @@ -0,0 +1,84 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my $underscore_test = shift @ARGV || ""; +if (!-d $underscore_test) { + print <<_END_; +Usage: + + testify ./underscore/test + + # Should look something like: + arrays.js + chaining.js + collections.js + functions.js + index.html + objects.js + speed.js + utility.js + vendor + +_END_ + if ($underscore_test) { + die "!: Not a directory: $underscore_test\n" + } + exit; +} + +chdir $underscore_test or die "!: $!"; + +my @js = <*.js>; + +for my $file (@js) { + open my $fh, '<', $file or die "!: $!"; + my $tests = join "", <$fh>; + my @tests = $tests =~ m/ + ^(\s{2}test\(.*? + ^\s{2}}\);)$ + /mgxs; + close $fh; + next unless @tests; + print "$file: ", scalar(@tests), "\n"; + my $underscore_name = "underscore_$file"; + $underscore_name =~ s/.js$//; + my $go_file = "${underscore_name}_test.go"; + $go_file =~ s/.js$/.go/; + open $fh, '>', $go_file or die "!: $!"; + + $fh->print(<<_END_); +package otto + +import ( + "testing" +) + +_END_ + + my $count = 0; + for my $test (@tests) { + $test =~ s/`([^`]+)`/<$1>/g; + my ($name) = $test =~ m/^\s*test\(['"]([^'"]+)['"]/; + $fh->print(<<_END_); +// $name +func Test_${underscore_name}_$count(t *testing.T) { + tt(t, func(){ + test := underscoreTest() + + test(` +$test + `) + }) +} + +_END_ + $count++; + } +} + +# test('#779 - delimeters are applied to unescaped text.', 1, function() { +# var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); +# strictEqual(template(), '<<\nx\n>>'); +# }); diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore/underscore.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/underscore.go new file mode 100644 index 000000000..714b8f3cf --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore/underscore.go @@ -0,0 +1,49 @@ +/* +Package underscore contains the source for the JavaScript utility-belt library. + + import ( + _ "github.com/robertkrimen/otto/underscore" + ) + // Every Otto runtime will now include underscore + +http://underscorejs.org + +https://github.com/documentcloud/underscore + +By importing this package, you'll automatically load underscore every time you create a new Otto runtime. + +To prevent this behavior, you can do the following: + + import ( + "github.com/robertkrimen/otto/underscore" + ) + + func init() { + underscore.Disable() + } + +*/ +package underscore + +import ( + "github.com/robertkrimen/otto/registry" +) + +var entry *registry.Entry = registry.Register(func() string { + return Source() +}) + +// Enable underscore runtime inclusion. +func Enable() { + entry.Enable() +} + +// Disable underscore runtime inclusion. +func Disable() { + entry.Disable() +} + +// Source returns the underscore source. +func Source() string { + return string(underscore()) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_arrays_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_arrays_test.go new file mode 100644 index 000000000..9d6297820 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_arrays_test.go @@ -0,0 +1,344 @@ +package otto + +import ( + "testing" +) + +// first +func Test_underscore_arrays_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("first", function() { + equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); + equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); + equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); + equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); + var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); + equal(result, 4, 'works on an arguments object.'); + result = _.map([[1,2,3],[1,2,3]], _.first); + equal(result.join(','), '1,1', 'works well with _.map'); + result = (function() { return _.take([1,2,3], 2); })(); + equal(result.join(','), '1,2', 'aliased as take'); + + equal(_.first(null), undefined, 'handles nulls'); + }); + `) + }) +} + +// rest +func Test_underscore_arrays_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("rest", function() { + var numbers = [1, 2, 3, 4]; + equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); + equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); + equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); + var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); + equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.rest); + equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); + result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4); + equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object'); + }); + `) + }) +} + +// initial +func Test_underscore_arrays_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("initial", function() { + equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); + equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); + var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4); + equal(result.join(", "), "1, 2, 3", 'initial works on arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.initial); + equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); + }); + `) + }) +} + +// last +func Test_underscore_arrays_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("last", function() { + equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); + equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); + equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); + equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); + var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); + equal(result, 4, 'works on an arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.last); + equal(result.join(','), '3,3', 'works well with _.map'); + + equal(_.last(null), undefined, 'handles nulls'); + }); + `) + }) +} + +// compact +func Test_underscore_arrays_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("compact", function() { + equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3); + equal(result, 3, 'works on an arguments object'); + }); + `) + }) +} + +// flatten +func Test_underscore_arrays_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("flatten", function() { + var list = [1, [2], [3, [[[4]]]]]; + deepEqual(_.flatten(list), [1,2,3,4], 'can flatten nested arrays'); + deepEqual(_.flatten(list, true), [1,2,3,[[[4]]]], 'can shallowly flatten nested arrays'); + var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); + deepEqual(result, [1,2,3,4], 'works on an arguments object'); + }); + `) + }) +} + +// without +func Test_underscore_arrays_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("without", function() { + var list = [1, 2, 1, 0, 3, 1, 4]; + equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); + var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); + equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); + + var list = [{one : 1}, {two : 2}]; + ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); + ok(_.without(list, list[0]).length == 1, 'ditto.'); + }); + `) + }) +} + +// uniq +func Test_underscore_arrays_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("uniq", function() { + var list = [1, 2, 1, 3, 1, 4]; + equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); + + var list = [1, 1, 1, 2, 2, 3]; + equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + + var list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; + var iterator = function(value) { return value.name; }; + equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); + + equal(_.map(_.uniq(list, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); + + var iterator = function(value) { return value +1; }; + var list = [1, 2, 2, 3, 4, 4]; + equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); + + var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); + equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); + }); + `) + }) +} + +// intersection +func Test_underscore_arrays_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("intersection", function() { + var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; + equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); + equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); + var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); + equal(result.join(''), 'moe', 'works on an arguments object'); + }); + `) + }) +} + +// union +func Test_underscore_arrays_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("union", function() { + var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); + equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); + + var result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); + equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); + }); + `) + }) +} + +// difference +func Test_underscore_arrays_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("difference", function() { + var result = _.difference([1, 2, 3], [2, 30, 40]); + equal(result.join(' '), '1 3', 'takes the difference of two arrays'); + + var result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); + equal(result.join(' '), '3 4', 'takes the difference of three arrays'); + }); + `) + }) +} + +// zip +func Test_underscore_arrays_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('zip', function() { + var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; + var stooges = _.zip(names, ages, leaders); + equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); + }); + `) + }) +} + +// object +func Test_underscore_arrays_12(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('object', function() { + var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); + var shouldBe = {moe: 30, larry: 40, curly: 50}; + ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); + + result = _.object([['one', 1], ['two', 2], ['three', 3]]); + shouldBe = {one: 1, two: 2, three: 3}; + ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object'); + + var stooges = {moe: 30, larry: 40, curly: 50}; + ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object'); + + ok(_.isEqual(_.object(null), {}), 'handles nulls'); + }); + `) + }) +} + +// indexOf +func Test_underscore_arrays_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("indexOf", function() { + var numbers = [1, 2, 3]; + numbers.indexOf = null; + equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); + var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); + equal(result, 1, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); + + var numbers = [10, 20, 30, 40, 50], num = 35; + var index = _.indexOf(numbers, num, true); + equal(index, -1, '35 is not in the list'); + + numbers = [10, 20, 30, 40, 50]; num = 40; + index = _.indexOf(numbers, num, true); + equal(index, 3, '40 is in the list'); + + numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; + index = _.indexOf(numbers, num, true); + equal(index, 1, '40 is in the list'); + + numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + index = _.indexOf(numbers, 2, 5); + equal(index, 7, 'supports the fromIndex argument'); + }); + `) + }) +} + +// lastIndexOf +func Test_underscore_arrays_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("lastIndexOf", function() { + var numbers = [1, 0, 1]; + equal(_.lastIndexOf(numbers, 1), 2); + + numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; + numbers.lastIndexOf = null; + equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); + equal(result, 5, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); + + numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + var index = _.lastIndexOf(numbers, 2, 2); + equal(index, 1, 'supports the fromIndex argument'); + }); + `) + }) +} + +// range +func Test_underscore_arrays_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("range", function() { + equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); + equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + equal(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); + equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); + }); + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_chaining_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_chaining_test.go new file mode 100644 index 000000000..accf04fdc --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_chaining_test.go @@ -0,0 +1,95 @@ +package otto + +import ( + "testing" +) + +// map/flatten/reduce +func Test_underscore_chaining_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("map/flatten/reduce", function() { + var lyrics = [ + "I'm a lumberjack and I'm okay", + "I sleep all night and I work all day", + "He's a lumberjack and he's okay", + "He sleeps all night and he works all day" + ]; + var counts = _(lyrics).chain() + .map(function(line) { return line.split(''); }) + .flatten() + .reduce(function(hash, l) { + hash[l] = hash[l] || 0; + hash[l]++; + return hash; + }, {}).value(); + ok(counts['a'] == 16 && counts['e'] == 10, 'counted all the letters in the song'); + }); + `) + }) +} + +// select/reject/sortBy +func Test_underscore_chaining_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("select/reject/sortBy", function() { + var numbers = [1,2,3,4,5,6,7,8,9,10]; + numbers = _(numbers).chain().select(function(n) { + return n % 2 == 0; + }).reject(function(n) { + return n % 4 == 0; + }).sortBy(function(n) { + return -n; + }).value(); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + }); + `) + }) +} + +// select/reject/sortBy in functional style +func Test_underscore_chaining_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("select/reject/sortBy in functional style", function() { + var numbers = [1,2,3,4,5,6,7,8,9,10]; + numbers = _.chain(numbers).select(function(n) { + return n % 2 == 0; + }).reject(function(n) { + return n % 4 == 0; + }).sortBy(function(n) { + return -n; + }).value(); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + }); + `) + }) +} + +// reverse/concat/unshift/pop/map +func Test_underscore_chaining_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("reverse/concat/unshift/pop/map", function() { + var numbers = [1,2,3,4,5]; + numbers = _(numbers).chain() + .reverse() + .concat([5, 5, 5]) + .unshift(17) + .pop() + .map(function(n){ return n * 2; }) + .value(); + equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); + }); + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_collections_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_collections_test.go new file mode 100644 index 000000000..9afc2a2b2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_collections_test.go @@ -0,0 +1,698 @@ +package otto + +import ( + "testing" +) + +// each +func Test_underscore_collections_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("each", function() { + _.each([1, 2, 3], function(num, i) { + equal(num, i + 1, 'each iterators provide value and iteration count'); + }); + + var answers = []; + _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); + equal(answers.join(', '), '5, 10, 15', 'context object property accessed'); + + answers = []; + _.forEach([1, 2, 3], function(num){ answers.push(num); }); + equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); + + answers = []; + var obj = {one : 1, two : 2, three : 3}; + obj.constructor.prototype.four = 4; + _.each(obj, function(value, key){ answers.push(key); }); + equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); + delete obj.constructor.prototype.four; + + var answer = null; + _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); + ok(answer, 'can reference the original collection from inside the iterator'); + + answers = 0; + _.each(null, function(){ ++answers; }); + equal(answers, 0, 'handles a null properly'); + }); + `) + }) +} + +// map +func Test_underscore_collections_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('map', function() { + var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'doubled numbers'); + + doubled = _.collect([1, 2, 3], function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); + + var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); + equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + + var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); + + // TEST: ReferenceError: document is not defined + return; + + if (document.querySelectorAll) { + var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; }); + deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.'); + } + + var ids = _.map($('#map-test').children(), function(n){ return n.id; }); + deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on jQuery Array-likes.'); + + var ids = _.map(document.images, function(n){ return n.id; }); + ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections'); + + var ifnull = _.map(null, function(){}); + ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); + }); + `) + }) +} + +// reduce +func Test_underscore_collections_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reduce', function() { + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'can sum up an array'); + + var context = {multiplier : 3}; + sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); + equal(sum, 18, 'can reduce with a context object'); + + sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'aliased as "inject"'); + + sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'OO-style reduce'); + + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); + equal(sum, 6, 'default initial value'); + + var ifnull; + try { + _.reduce(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + }); + `) + }) +} + +// reduceRight +func Test_underscore_collections_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reduceRight', function() { + var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equal(list, 'bazbarfoo', 'can perform right folds'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equal(list, 'bazbarfoo', 'aliased as "foldr"'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); + equal(list, 'bazbarfoo', 'default initial value'); + + var ifnull; + try { + _.reduceRight(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; }); + equal(sum, 6, 'default initial value on object'); + + ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + + equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + + // Assert that the correct arguments are being passed. + + var args, + memo = {}, + object = {a: 1, b: 2}, + lastKey = _.keys(object).pop(); + + var expected = lastKey == 'a' + ? [memo, 1, 'a', object] + : [memo, 2, 'b', object]; + + _.reduceRight(object, function() { + args || (args = _.toArray(arguments)); + }, memo); + + deepEqual(args, expected); + + // And again, with numeric keys. + + object = {'2': 'a', '1': 'b'}; + lastKey = _.keys(object).pop(); + args = null; + + expected = lastKey == '2' + ? [memo, 'a', '2', object] + : [memo, 'b', '1', object]; + + _.reduceRight(object, function() { + args || (args = _.toArray(arguments)); + }, memo); + + deepEqual(args, expected); + }); + `) + }) +} + +// find +func Test_underscore_collections_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('find', function() { + var array = [1, 2, 3, 4]; + strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found '); + strictEqual(_.find(array, function() { return false; }), void 0, 'should return if is not found'); + }); + `) + }) +} + +// detect +func Test_underscore_collections_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('detect', function() { + var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); + equal(result, 2, 'found the first "2" and broke the loop'); + }); + `) + }) +} + +// select +func Test_underscore_collections_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('select', function() { + var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(evens.join(', '), '2, 4, 6', 'selected each even number'); + + evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); + }); + `) + }) +} + +// reject +func Test_underscore_collections_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reject', function() { + var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(odds.join(', '), '1, 3, 5', 'rejected each even number'); + + var context = "obj"; + + var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){ + equal(context, "obj"); + return num % 2 != 0; + }, context); + equal(evens.join(', '), '2, 4, 6', 'rejected each odd number'); + }); + `) + }) +} + +// all +func Test_underscore_collections_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('all', function() { + ok(_.all([], _.identity), 'the empty set'); + ok(_.all([true, true, true], _.identity), 'all true values'); + ok(!_.all([true, false, true], _.identity), 'one false value'); + ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); + ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number'); + ok(_.all([1], _.identity) === true, 'cast to boolean - true'); + ok(_.all([0], _.identity) === false, 'cast to boolean - false'); + ok(_.every([true, true, true], _.identity), 'aliased as "every"'); + ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined'); + }); + `) + }) +} + +// any +func Test_underscore_collections_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('any', function() { + var nativeSome = Array.prototype.some; + Array.prototype.some = null; + ok(!_.any([]), 'the empty set'); + ok(!_.any([false, false, false]), 'all false values'); + ok(_.any([false, false, true]), 'one true value'); + ok(_.any([null, 0, 'yes', false]), 'a string'); + ok(!_.any([null, 0, '', false]), 'falsy values'); + ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers'); + ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number'); + ok(_.any([1], _.identity) === true, 'cast to boolean - true'); + ok(_.any([0], _.identity) === false, 'cast to boolean - false'); + ok(_.some([false, false, true]), 'aliased as "some"'); + Array.prototype.some = nativeSome; + }); + `) + }) +} + +// include +func Test_underscore_collections_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('include', function() { + ok(_.include([1,2,3], 2), 'two is in the array'); + ok(!_.include([1,3,9], 2), 'two is not in the array'); + ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values'); + ok(_([1,2,3]).include(2), 'OO-style include'); + }); + `) + }) +} + +// invoke +func Test_underscore_collections_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, 'sort'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + `) + }) +} + +// invoke w/ function reference +func Test_underscore_collections_12(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke w/ function reference', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, Array.prototype.sort); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + `) + }) +} + +// invoke when strings have a call method +func Test_underscore_collections_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke when strings have a call method', function() { + String.prototype.call = function() { + return 42; + }; + var list = [[5, 1, 7], [3, 2, 1]]; + var s = "foo"; + equal(s.call(), 42, "call function exists"); + var result = _.invoke(list, 'sort'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + delete String.prototype.call; + equal(s.call, undefined, "call function removed"); + }); + `) + }) +} + +// pluck +func Test_underscore_collections_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('pluck', function() { + var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); + }); + `) + }) +} + +// where +func Test_underscore_collections_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('where', function() { + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + var result = _.where(list, {a: 1}); + equal(result.length, 3); + equal(result[result.length - 1].b, 4); + result = _.where(list, {b: 2}); + equal(result.length, 2); + equal(result[0].a, 1); + }); + `) + }) +} + +// findWhere +func Test_underscore_collections_16(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('findWhere', function() { + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; + var result = _.findWhere(list, {a: 1}); + deepEqual(result, {a: 1, b: 2}); + result = _.findWhere(list, {b: 4}); + deepEqual(result, {a: 1, b: 4}); + }); + `) + }) +} + +// max +func Test_underscore_collections_17(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('max', function() { + equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + + var neg = _.max([1, 2, 3], function(num){ return -num; }); + equal(neg, 1, 'can perform a computation-based max'); + + equal(-Infinity, _.max({}), 'Maximum value of an empty object'); + equal(-Infinity, _.max([]), 'Maximum value of an empty array'); + equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); + + // TEST: Takes too long + return; + + equal(299999, _.max(_.range(1,300000)), "Maximum value of a too-big array"); + }); + `) + }) +} + +// min +func Test_underscore_collections_18(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('min', function() { + equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + + var neg = _.min([1, 2, 3], function(num){ return -num; }); + equal(neg, 3, 'can perform a computation-based min'); + + equal(Infinity, _.min({}), 'Minimum value of an empty object'); + equal(Infinity, _.min([]), 'Minimum value of an empty array'); + equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + + var now = new Date(9999999999); + var then = new Date(0); + equal(_.min([now, then]), then); + + // TEST: Takes too long + return; + + equal(1, _.min(_.range(1,300000)), "Minimum value of a too-big array"); + }); + `) + }) +} + +// sortBy +func Test_underscore_collections_19(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('sortBy', function() { + var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; + people = _.sortBy(people, function(person){ return person.age; }); + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + + var list = [undefined, 4, 1, undefined, 3, 2]; + equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values'); + + var list = ["one", "two", "three", "four", "five"]; + var sorted = _.sortBy(list, 'length'); + equal(sorted.join(' '), 'one two four five three', 'sorted by length'); + + function Pair(x, y) { + this.x = x; + this.y = y; + } + + var collection = [ + new Pair(1, 1), new Pair(1, 2), + new Pair(1, 3), new Pair(1, 4), + new Pair(1, 5), new Pair(1, 6), + new Pair(2, 1), new Pair(2, 2), + new Pair(2, 3), new Pair(2, 4), + new Pair(2, 5), new Pair(2, 6), + new Pair(undefined, 1), new Pair(undefined, 2), + new Pair(undefined, 3), new Pair(undefined, 4), + new Pair(undefined, 5), new Pair(undefined, 6) + ]; + + var actual = _.sortBy(collection, function(pair) { + return pair.x; + }); + + deepEqual(actual, collection, 'sortBy should be stable'); + }); + `) + }) +} + +// groupBy +func Test_underscore_collections_20(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('groupBy', function() { + var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); + ok('0' in parity && '1' in parity, 'created a group for each value'); + equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); + + var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var grouped = _.groupBy(list, 'length'); + equal(grouped['3'].join(' '), 'one two six ten'); + equal(grouped['4'].join(' '), 'four five nine'); + equal(grouped['5'].join(' '), 'three seven eight'); + + var context = {}; + _.groupBy([{}], function(){ ok(this === context); }, context); + + grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { + return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; + }); + equal(grouped.constructor.length, 1); + equal(grouped.hasOwnProperty.length, 2); + + var array = [{}]; + _.groupBy(array, function(value, index, obj){ ok(obj === array); }); + + var array = [1, 2, 1, 2, 3]; + var grouped = _.groupBy(array); + equal(grouped['1'].length, 2); + equal(grouped['3'].length, 1); + }); + `) + }) +} + +// countBy +func Test_underscore_collections_21(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('countBy', function() { + var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; }); + equal(parity['true'], 2); + equal(parity['false'], 3); + + var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var grouped = _.countBy(list, 'length'); + equal(grouped['3'], 4); + equal(grouped['4'], 3); + equal(grouped['5'], 3); + + var context = {}; + _.countBy([{}], function(){ ok(this === context); }, context); + + grouped = _.countBy([4.2, 6.1, 6.4], function(num) { + return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; + }); + equal(grouped.constructor, 1); + equal(grouped.hasOwnProperty, 2); + + var array = [{}]; + _.countBy(array, function(value, index, obj){ ok(obj === array); }); + + var array = [1, 2, 1, 2, 3]; + var grouped = _.countBy(array); + equal(grouped['1'], 2); + equal(grouped['3'], 1); + }); + `) + }) +} + +// sortedIndex +func Test_underscore_collections_22(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('sortedIndex', function() { + var numbers = [10, 20, 30, 40, 50], num = 35; + var indexForNum = _.sortedIndex(numbers, num); + equal(indexForNum, 3, '35 should be inserted at index 3'); + + var indexFor30 = _.sortedIndex(numbers, 30); + equal(indexFor30, 2, '30 should be inserted at index 2'); + + var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; + var iterator = function(obj){ return obj.x; }; + strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); + strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); + + var context = {1: 2, 2: 3, 3: 4}; + iterator = function(obj){ return this[obj]; }; + strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); + }); + `) + }) +} + +// shuffle +func Test_underscore_collections_23(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('shuffle', function() { + var numbers = _.range(10); + var shuffled = _.shuffle(numbers).sort(); + notStrictEqual(numbers, shuffled, 'original object is unmodified'); + equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); + }); + `) + }) +} + +// toArray +func Test_underscore_collections_24(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('toArray', function() { + ok(!_.isArray(arguments), 'arguments object is not an array'); + ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); + var a = [1,2,3]; + ok(_.toArray(a) !== a, 'array is cloned'); + equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); + + var numbers = _.toArray({one : 1, two : 2, three : 3}); + equal(numbers.join(', '), '1, 2, 3', 'object flattened into array'); + + // TEST: ReferenceError: document is not defined + return; + + // test in IE < 9 + try { + var actual = _.toArray(document.childNodes); + } catch(ex) { } + + ok(_.isArray(actual), 'should not throw converting a node list'); + }); + `) + }) +} + +// size +func Test_underscore_collections_25(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('size', function() { + equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); + equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); + + var func = function() { + return _.size(arguments); + }; + + equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); + + equal(_.size('hello'), 5, 'can compute the size of a string'); + + equal(_.size(null), 0, 'handles nulls'); + }); + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_functions_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_functions_test.go new file mode 100644 index 000000000..be59e16b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_functions_test.go @@ -0,0 +1,208 @@ +package otto + +import ( + "testing" +) + +// bind +func Test_underscore_functions_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("bind", function() { + var context = {name : 'moe'}; + var func = function(arg) { return "name: " + (this.name || arg); }; + var bound = _.bind(func, context); + equal(bound(), 'name: moe', 'can bind a function to a context'); + + bound = _(func).bind(context); + equal(bound(), 'name: moe', 'can do OO-style binding'); + + bound = _.bind(func, null, 'curly'); + equal(bound(), 'name: curly', 'can bind without specifying a context'); + + func = function(salutation, name) { return salutation + ': ' + name; }; + func = _.bind(func, this, 'hello'); + equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + + func = _.bind(func, this, 'curly'); + equal(func(), 'hello: curly', 'the function was completely applied in advance'); + + func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; + func = _.bind(func, this, 'hello', 'moe', 'curly'); + equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + + func = function(context, message) { equal(this, context, message); }; + _.bind(func, 0, 0, 'can bind a function to <0>')(); + _.bind(func, '', '', 'can bind a function to an empty string')(); + _.bind(func, false, false, 'can bind a function to ')(); + + // These tests are only meaningful when using a browser without a native bind function + // To test this with a modern browser, set underscore's nativeBind to undefined + var F = function () { return this; }; + var Boundf = _.bind(F, {hello: "moe curly"}); + equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); + }); + `) + }) +} + +// partial +func Test_underscore_functions_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("partial", function() { + var obj = {name: 'moe'}; + var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; + + obj.func = _.partial(func, 'a', 'b'); + equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); + }); + `) + }) +} + +// bindAll +func Test_underscore_functions_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("bindAll", function() { + var curly = {name : 'curly'}, moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + curly.getName = moe.getName; + _.bindAll(moe, 'getName', 'sayHi'); + curly.sayHi = moe.sayHi; + equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); + equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + + curly = {name : 'curly'}; + moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + _.bindAll(moe); + curly.sayHi = moe.sayHi; + equal(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); + }); + `) + }) +} + +// memoize +func Test_underscore_functions_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("memoize", function() { + var fib = function(n) { + return n < 2 ? n : fib(n - 1) + fib(n - 2); + }; + var fastFib = _.memoize(fib); + equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + equal(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); + + var o = function(str) { + return str; + }; + var fastO = _.memoize(o); + equal(o('toString'), 'toString', 'checks hasOwnProperty'); + equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); + }); + `) + }) +} + +// once +func Test_underscore_functions_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("once", function() { + var num = 0; + var increment = _.once(function(){ num++; }); + increment(); + increment(); + equal(num, 1); + }); + `) + }) +} + +// wrap +func Test_underscore_functions_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("wrap", function() { + var greet = function(name){ return "hi: " + name; }; + var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); + equal(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); + + var inner = function(){ return "Hello "; }; + var obj = {name : "Moe"}; + obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); + equal(obj.hi(), "Hello Moe"); + + var noop = function(){}; + var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); + var ret = wrapped(['whats', 'your'], 'vector', 'victor'); + deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); + }); + `) + }) +} + +// compose +func Test_underscore_functions_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("compose", function() { + var greet = function(name){ return "hi: " + name; }; + var exclaim = function(sentence){ return sentence + '!'; }; + var composed = _.compose(exclaim, greet); + equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + + composed = _.compose(greet, exclaim); + equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + }); + `) + }) +} + +// after +func Test_underscore_functions_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("after", function() { + var testAfter = function(afterAmount, timesCalled) { + var afterCalled = 0; + var after = _.after(afterAmount, function() { + afterCalled++; + }); + while (timesCalled--) after(); + return afterCalled; + }; + + equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); + equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); + equal(testAfter(0, 0), 1, "after(0) should fire immediately"); + }); + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_objects_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_objects_test.go new file mode 100644 index 000000000..ae9c5d5ff --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_objects_test.go @@ -0,0 +1,822 @@ +package otto + +import ( + "testing" +) + +// keys +func Test_underscore_objects_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("keys", function() { + equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); + // the test above is not safe because it relies on for-in enumeration order + var a = []; a[1] = 0; + equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); + raises(function() { _.keys(null); }, TypeError, 'throws an error for values'); + raises(function() { _.keys(void 0); }, TypeError, 'throws an error for values'); + raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives'); + raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives'); + raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives'); + }); + `) + }) +} + +// values +func Test_underscore_objects_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("values", function() { + equal(_.values({one: 1, two: 2}).join(', '), '1, 2', 'can extract the values from an object'); + equal(_.values({one: 1, two: 2, length: 3}).join(', '), '1, 2, 3', '... even when one of them is "length"'); + }); + `) + }) +} + +// pairs +func Test_underscore_objects_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("pairs", function() { + deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); + deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); + }); + `) + }) +} + +// invert +func Test_underscore_objects_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("invert", function() { + var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; + equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object'); + ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started'); + + var obj = {length: 3}; + ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"') + }); + `) + }) +} + +// functions +func Test_underscore_objects_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("functions", function() { + var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; + ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); + + var Animal = function(){}; + Animal.prototype.run = function(){}; + equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); + }); + `) + }) +} + +// extend +func Test_underscore_objects_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("extend", function() { + var result; + equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); + equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); + equal(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); + result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); + ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); + result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); + ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); + result = _.extend({}, {a: void 0, b: null}); + equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); + + try { + result = {}; + _.extend(result, null, undefined, {a:1}); + } catch(ex) {} + + equal(result.a, 1, 'should not error on or sources'); + }); + `) + }) +} + +// pick +func Test_underscore_objects_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("pick", function() { + var result; + result = _.pick({a:1, b:2, c:3}, 'a', 'c'); + ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); + result = _.pick({a:1, b:2, c:3}, ['b', 'c']); + ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); + result = _.pick({a:1, b:2, c:3}, ['a'], 'b'); + ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); + + var Obj = function(){}; + Obj.prototype = {a: 1, b: 2, c: 3}; + ok(_.isEqual(_.pick(new Obj, 'a', 'c'), {a:1, c: 3}), 'include prototype props'); + }); + `) + }) +} + +// omit +func Test_underscore_objects_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("omit", function() { + var result; + result = _.omit({a:1, b:2, c:3}, 'b'); + ok(_.isEqual(result, {a:1, c:3}), 'can omit a single named property'); + result = _.omit({a:1, b:2, c:3}, 'a', 'c'); + ok(_.isEqual(result, {b:2}), 'can omit several named properties'); + result = _.omit({a:1, b:2, c:3}, ['b', 'c']); + ok(_.isEqual(result, {a:1}), 'can omit properties named in an array'); + + var Obj = function(){}; + Obj.prototype = {a: 1, b: 2, c: 3}; + ok(_.isEqual(_.omit(new Obj, 'b'), {a:1, c: 3}), 'include prototype props'); + }); + `) + }) +} + +// defaults +func Test_underscore_objects_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("defaults", function() { + var result; + var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; + + _.defaults(options, {zero: 1, one: 10, twenty: 20}); + equal(options.zero, 0, 'value exists'); + equal(options.one, 1, 'value exists'); + equal(options.twenty, 20, 'default applied'); + + _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); + equal(options.empty, "", 'value exists'); + ok(_.isNaN(options.nan), "NaN isn't overridden"); + equal(options.word, "word", 'new value is added, first one wins'); + + try { + options = {}; + _.defaults(options, null, undefined, {a:1}); + } catch(ex) {} + + equal(options.a, 1, 'should not error on or sources'); + }); + `) + }) +} + +// clone +func Test_underscore_objects_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("clone", function() { + var moe = {name : 'moe', lucky : [13, 27, 34]}; + var clone = _.clone(moe); + equal(clone.name, 'moe', 'the clone as the attributes of the original'); + + clone.name = 'curly'; + ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); + + clone.lucky.push(101); + equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + + equal(_.clone(undefined), void 0, 'non objects should not be changed by clone'); + equal(_.clone(1), 1, 'non objects should not be changed by clone'); + equal(_.clone(null), null, 'non objects should not be changed by clone'); + }); + `) + }) +} + +// isEqual +func Test_underscore_objects_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isEqual", function() { + function First() { + this.value = 1; + } + First.prototype.value = 1; + function Second() { + this.value = 1; + } + Second.prototype.value = 2; + + // Basic equality and identity comparisons. + ok(_.isEqual(null, null), " is equal to "); + ok(_.isEqual(), " is equal to "); + + ok(!_.isEqual(0, -0), "<0> is not equal to <-0>"); + ok(!_.isEqual(-0, 0), "Commutative equality is implemented for <0> and <-0>"); + ok(!_.isEqual(null, undefined), " is not equal to "); + ok(!_.isEqual(undefined, null), "Commutative equality is implemented for and "); + + // String object and primitive comparisons. + ok(_.isEqual("Curly", "Curly"), "Identical string primitives are equal"); + ok(_.isEqual(new String("Curly"), new String("Curly")), "String objects with identical primitive values are equal"); + ok(_.isEqual(new String("Curly"), "Curly"), "String primitives and their corresponding object wrappers are equal"); + ok(_.isEqual("Curly", new String("Curly")), "Commutative equality is implemented for string objects and primitives"); + + ok(!_.isEqual("Curly", "Larry"), "String primitives with different values are not equal"); + ok(!_.isEqual(new String("Curly"), new String("Larry")), "String objects with different primitive values are not equal"); + ok(!_.isEqual(new String("Curly"), {toString: function(){ return "Curly"; }}), "String objects and objects with a custom method are not equal"); + + // Number object and primitive comparisons. + ok(_.isEqual(75, 75), "Identical number primitives are equal"); + ok(_.isEqual(new Number(75), new Number(75)), "Number objects with identical primitive values are equal"); + ok(_.isEqual(75, new Number(75)), "Number primitives and their corresponding object wrappers are equal"); + ok(_.isEqual(new Number(75), 75), "Commutative equality is implemented for number objects and primitives"); + ok(!_.isEqual(new Number(0), -0), " and <-0> are not equal"); + ok(!_.isEqual(0, new Number(-0)), "Commutative equality is implemented for and <-0>"); + + ok(!_.isEqual(new Number(75), new Number(63)), "Number objects with different primitive values are not equal"); + ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), "Number objects and objects with a method are not equal"); + + // Comparisons involving . + ok(_.isEqual(NaN, NaN), " is equal to "); + ok(!_.isEqual(61, NaN), "A number primitive is not equal to "); + ok(!_.isEqual(new Number(79), NaN), "A number object is not equal to "); + ok(!_.isEqual(Infinity, NaN), " is not equal to "); + + // Boolean object and primitive comparisons. + ok(_.isEqual(true, true), "Identical boolean primitives are equal"); + ok(_.isEqual(new Boolean, new Boolean), "Boolean objects with identical primitive values are equal"); + ok(_.isEqual(true, new Boolean(true)), "Boolean primitives and their corresponding object wrappers are equal"); + ok(_.isEqual(new Boolean(true), true), "Commutative equality is implemented for booleans"); + ok(!_.isEqual(new Boolean(true), new Boolean), "Boolean objects with different primitive values are not equal"); + + // Common type coercions. + ok(!_.isEqual(true, new Boolean(false)), "Boolean objects are not equal to the boolean primitive "); + ok(!_.isEqual("75", 75), "String and number primitives with like values are not equal"); + ok(!_.isEqual(new Number(63), new String(63)), "String and number objects with like values are not equal"); + ok(!_.isEqual(75, "75"), "Commutative equality is implemented for like string and number values"); + ok(!_.isEqual(0, ""), "Number and string primitives with like values are not equal"); + ok(!_.isEqual(1, true), "Number and boolean primitives with like values are not equal"); + ok(!_.isEqual(new Boolean(false), new Number(0)), "Boolean and number objects with like values are not equal"); + ok(!_.isEqual(false, new String("")), "Boolean primitives and string objects with like values are not equal"); + ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), "Dates and their corresponding numeric primitive values are not equal"); + + // Dates. + ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), "Date objects referencing identical times are equal"); + ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), "Date objects referencing different times are not equal"); + ok(!_.isEqual(new Date(2009, 11, 13), { + getTime: function(){ + return 12606876e5; + } + }), "Date objects and objects with a method are not equal"); + ok(!_.isEqual(new Date("Curly"), new Date("Curly")), "Invalid dates are not equal"); + + // Functions. + ok(!_.isEqual(First, Second), "Different functions with identical bodies and source code representations are not equal"); + + // RegExps. + ok(_.isEqual(/(?:)/gim, /(?:)/gim), "RegExps with equivalent patterns and flags are equal"); + ok(!_.isEqual(/(?:)/g, /(?:)/gi), "RegExps with equivalent patterns and different flags are not equal"); + ok(!_.isEqual(/Moe/gim, /Curly/gim), "RegExps with different patterns and equivalent flags are not equal"); + ok(!_.isEqual(/(?:)/gi, /(?:)/g), "Commutative equality is implemented for RegExps"); + ok(!_.isEqual(/Curly/g, {source: "Larry", global: true, ignoreCase: false, multiline: false}), "RegExps and RegExp-like objects are not equal"); + + // Empty arrays, array-like objects, and object literals. + ok(_.isEqual({}, {}), "Empty object literals are equal"); + ok(_.isEqual([], []), "Empty array literals are equal"); + ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects are equal"); + ok(!_.isEqual({length: 0}, []), "Array-like objects and arrays are not equal."); + ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); + + ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); + ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); + + // Arrays with primitive and object values. + ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal"); + ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); + + // Multi-dimensional arrays. + var a = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + var b = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + ok(_.isEqual(a, b), "Arrays containing nested arrays and objects are recursively compared"); + + // Overwrite the methods defined in ES 5.1 section 15.4.4. + a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null; + b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; + + // Array elements and properties. + ok(_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are equal"); + a.push("White Rocks"); + ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); + a.push("East Boulder"); + b.push("Gunbarrel Ranch", "Teller Farm"); + ok(!_.isEqual(a, b), "Arrays of identical lengths containing different elements are not equal"); + + // Sparse arrays. + ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); + ok(!_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are not equal when both are empty"); + + // Simple objects. + ok(_.isEqual({a: "Curly", b: 1, c: true}, {a: "Curly", b: 1, c: true}), "Objects containing identical primitives are equal"); + ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), "Objects containing equivalent members are equal"); + ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), "Objects of identical sizes with different values are not equal"); + ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), "Objects of identical sizes with different property names are not equal"); + ok(!_.isEqual({a: 1, b: 2}, {a: 1}), "Objects of different sizes are not equal"); + ok(!_.isEqual({a: 1}, {a: 1, b: 2}), "Commutative equality is implemented for objects"); + ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), "Objects with identical keys and different values are not equivalent"); + + // contains nested objects and arrays. + a = { + name: new String("Moe Howard"), + age: new Number(77), + stooge: true, + hobbies: ["acting"], + film: { + name: "Sing a Song of Six Pants", + release: new Date(1947, 9, 30), + stars: [new String("Larry Fine"), "Shemp Howard"], + minutes: new Number(16), + seconds: 54 + } + }; + + // contains equivalent nested objects and arrays. + b = { + name: new String("Moe Howard"), + age: new Number(77), + stooge: true, + hobbies: ["acting"], + film: { + name: "Sing a Song of Six Pants", + release: new Date(1947, 9, 30), + stars: [new String("Larry Fine"), "Shemp Howard"], + minutes: new Number(16), + seconds: 54 + } + }; + ok(_.isEqual(a, b), "Objects with nested equivalent members are recursively compared"); + + // Instances. + ok(_.isEqual(new First, new First), "Object instances are equal"); + ok(!_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are not equal"); + ok(!_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are not equal"); + ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined"); + + // Circular Arrays. + (a = []).push(a); + (b = []).push(b); + ok(_.isEqual(a, b), "Arrays containing circular references are equal"); + a.push(new String("Larry")); + b.push(new String("Larry")); + ok(_.isEqual(a, b), "Arrays containing circular references and equivalent properties are equal"); + a.push("Shemp"); + b.push("Curly"); + ok(!_.isEqual(a, b), "Arrays containing circular references and different properties are not equal"); + + // More circular arrays #767. + a = ["everything is checked but", "this", "is not"]; + a[1] = a; + b = ["everything is checked but", ["this", "array"], "is not"]; + ok(!_.isEqual(a, b), "Comparison of circular references with non-circular references are not equal"); + + // Circular Objects. + a = {abc: null}; + b = {abc: null}; + a.abc = a; + b.abc = b; + ok(_.isEqual(a, b), "Objects containing circular references are equal"); + a.def = 75; + b.def = 75; + ok(_.isEqual(a, b), "Objects containing circular references and equivalent properties are equal"); + a.def = new Number(75); + b.def = new Number(63); + ok(!_.isEqual(a, b), "Objects containing circular references and different properties are not equal"); + + // More circular objects #767. + a = {everything: "is checked", but: "this", is: "not"}; + a.but = a; + b = {everything: "is checked", but: {that:"object"}, is: "not"}; + ok(!_.isEqual(a, b), "Comparison of circular references with non-circular object references are not equal"); + + // Cyclic Structures. + a = [{abc: null}]; + b = [{abc: null}]; + (a[0].abc = a).push(a); + (b[0].abc = b).push(b); + ok(_.isEqual(a, b), "Cyclic structures are equal"); + a[0].def = "Larry"; + b[0].def = "Larry"; + ok(_.isEqual(a, b), "Cyclic structures containing equivalent properties are equal"); + a[0].def = new String("Larry"); + b[0].def = new String("Curly"); + ok(!_.isEqual(a, b), "Cyclic structures containing different properties are not equal"); + + // Complex Circular References. + a = {foo: {b: {foo: {c: {foo: null}}}}}; + b = {foo: {b: {foo: {c: {foo: null}}}}}; + a.foo.b.foo.c.foo = a; + b.foo.b.foo.c.foo = b; + ok(_.isEqual(a, b), "Cyclic structures with nested and identically-named properties are equal"); + + // Chaining. + ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + + a = _({x: 1, y: 2}).chain(); + b = _({x: 1, y: 2}).chain(); + equal(_.isEqual(a.isEqual(b), _(true)), true, ' can be chained'); + + // TEST: ??? + return; + + // Objects from another frame. + ok(_.isEqual({}, iObject)); + }); + `) + }) +} + +// isEmpty +func Test_underscore_objects_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isEmpty", function() { + ok(!_([1]).isEmpty(), '[1] is not empty'); + ok(_.isEmpty([]), '[] is empty'); + ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); + ok(_.isEmpty({}), '{} is empty'); + ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); + ok(_.isEmpty(null), 'null is empty'); + ok(_.isEmpty(), 'undefined is empty'); + ok(_.isEmpty(''), 'the empty string is empty'); + ok(!_.isEmpty('moe'), 'but other strings are not'); + + var obj = {one : 1}; + delete obj.one; + ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + }); + `) + }) +} + +// isElement +func Test_underscore_objects_12(t *testing.T) { + // TEST: ReferenceError: $ is not defined + return + + tt(t, func() { + test, _ := test_() + + test(` + test("isElement", function() { + ok(!_.isElement('div'), 'strings are not dom elements'); + ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); + ok(_.isElement(iElement), 'even from another frame'); + }); + `) + }) +} + +// isArguments +func Test_underscore_objects_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isArguments", function() { + var args = (function(){ return arguments; })(1, 2, 3); + ok(!_.isArguments('string'), 'a string is not an arguments object'); + ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); + ok(_.isArguments(args), 'but the arguments object is an arguments object'); + ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); + + // TEST: ReferenceError: iArguments is not defined + return; + ok(_.isArguments(iArguments), 'even from another frame'); + }); + `) + }) +} + +// isObject +func Test_underscore_objects_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isObject", function() { + ok(_.isObject(arguments), 'the arguments object is object'); + ok(_.isObject([1, 2, 3]), 'and arrays'); + // TEST: ReferenceError: $ is not defined + return; + ok(_.isObject($('html')[0]), 'and DOM element'); + ok(_.isObject(iElement), 'even from another frame'); + ok(_.isObject(function () {}), 'and functions'); + ok(_.isObject(iFunction), 'even from another frame'); + ok(!_.isObject(null), 'but not null'); + ok(!_.isObject(undefined), 'and not undefined'); + ok(!_.isObject('string'), 'and not string'); + ok(!_.isObject(12), 'and not number'); + ok(!_.isObject(true), 'and not boolean'); + ok(_.isObject(new String('string')), 'but new String()'); + }); + `) + }) +} + +// isArray +func Test_underscore_objects_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isArray", function() { + ok(!_.isArray(arguments), 'the arguments object is not an array'); + ok(_.isArray([1, 2, 3]), 'but arrays are'); + // TEST: ??? + return; + ok(_.isArray(iArray), 'even from another frame'); + }); + `) + }) +} + +// isString +func Test_underscore_objects_16(t *testing.T) { + // TEST: ReferenceError: document is not defined + return + + tt(t, func() { + test, _ := test_() + + test(` + test("isString", function() { + ok(!_.isString(document.body), 'the document body is not a string'); + ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); + // TEST: ??? + return; + ok(_.isString(iString), 'even from another frame'); + }); + `) + }) +} + +// isNumber +func Test_underscore_objects_17(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNumber", function() { + ok(!_.isNumber('string'), 'a string is not a number'); + ok(!_.isNumber(arguments), 'the arguments object is not a number'); + ok(!_.isNumber(undefined), 'undefined is not a number'); + ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); + ok(_.isNumber(NaN), 'NaN *is* a number'); + ok(_.isNumber(Infinity), 'Infinity is a number'); + // TEST: ??? + return; + ok(_.isNumber(iNumber), 'even from another frame'); + ok(!_.isNumber('1'), 'numeric strings are not numbers'); + }); + `) + }) +} + +// isBoolean +func Test_underscore_objects_18(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isBoolean", function() { + ok(!_.isBoolean(2), 'a number is not a boolean'); + ok(!_.isBoolean("string"), 'a string is not a boolean'); + ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); + ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); + ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); + ok(!_.isBoolean(undefined), 'undefined is not a boolean'); + ok(!_.isBoolean(NaN), 'NaN is not a boolean'); + ok(!_.isBoolean(null), 'null is not a boolean'); + ok(_.isBoolean(true), 'but true is'); + ok(_.isBoolean(false), 'and so is false'); + // TEST: ??? + return; + ok(_.isBoolean(iBoolean), 'even from another frame'); + }); + `) + }) +} + +// isFunction +func Test_underscore_objects_19(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isFunction", function() { + ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); + ok(!_.isFunction('moe'), 'strings are not functions'); + ok(_.isFunction(_.isFunction), 'but functions are'); + // TEST: ??? + return; + ok(_.isFunction(iFunction), 'even from another frame'); + }); + `) + }) +} + +// isDate +func Test_underscore_objects_20(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isDate", function() { + ok(!_.isDate(100), 'numbers are not dates'); + ok(!_.isDate({}), 'objects are not dates'); + ok(_.isDate(new Date()), 'but dates are'); + // TEST: ??? + return; + ok(_.isDate(iDate), 'even from another frame'); + }); + `) + }) +} + +// isRegExp +func Test_underscore_objects_21(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isRegExp", function() { + ok(!_.isRegExp(_.identity), 'functions are not RegExps'); + ok(_.isRegExp(/identity/), 'but RegExps are'); + // TEST: ??? + return; + ok(_.isRegExp(iRegExp), 'even from another frame'); + }); + `) + }) +} + +// isFinite +func Test_underscore_objects_22(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isFinite", function() { + ok(!_.isFinite(undefined), 'undefined is not Finite'); + ok(!_.isFinite(null), 'null is not Finite'); + ok(!_.isFinite(NaN), 'NaN is not Finite'); + ok(!_.isFinite(Infinity), 'Infinity is not Finite'); + ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); + ok(_.isFinite('12'), 'Numeric strings are numbers'); + ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); + ok(!_.isFinite(''), 'Empty strings are not numbers'); + var obj = new Number(5); + ok(_.isFinite(obj), 'Number instances can be finite'); + ok(_.isFinite(0), '0 is Finite'); + ok(_.isFinite(123), 'Ints are Finite'); + ok(_.isFinite(-12.44), 'Floats are Finite'); + }); + `) + }) +} + +// isNaN +func Test_underscore_objects_23(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNaN", function() { + ok(!_.isNaN(undefined), 'undefined is not NaN'); + ok(!_.isNaN(null), 'null is not NaN'); + ok(!_.isNaN(0), '0 is not NaN'); + ok(_.isNaN(NaN), 'but NaN is'); + // TEST: ??? + return; + ok(_.isNaN(iNaN), 'even from another frame'); + ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); + }); + `) + }) +} + +// isNull +func Test_underscore_objects_24(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNull", function() { + ok(!_.isNull(undefined), 'undefined is not null'); + ok(!_.isNull(NaN), 'NaN is not null'); + ok(_.isNull(null), 'but null is'); + // TEST: ??? + return; + ok(_.isNull(iNull), 'even from another frame'); + }); + `) + }) +} + +// isUndefined +func Test_underscore_objects_25(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isUndefined", function() { + ok(!_.isUndefined(1), 'numbers are defined'); + ok(!_.isUndefined(null), 'null is defined'); + ok(!_.isUndefined(false), 'false is defined'); + ok(!_.isUndefined(NaN), 'NaN is defined'); + ok(_.isUndefined(), 'nothing is undefined'); + ok(_.isUndefined(undefined), 'undefined is undefined'); + // TEST: ??? + return; + ok(_.isUndefined(iUndefined), 'even from another frame'); + }); + `) + }) +} + +// tap +func Test_underscore_objects_26(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("tap", function() { + var intercepted = null; + var interceptor = function(obj) { intercepted = obj; }; + var returned = _.tap(1, interceptor); + equal(intercepted, 1, "passes tapped object to interceptor"); + equal(returned, 1, "returns tapped object"); + + returned = _([1,2,3]).chain(). + map(function(n){ return n * 2; }). + max(). + tap(interceptor). + value(); + ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); + }); + `) + }) +} + +// has +func Test_underscore_objects_27(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("has", function () { + var obj = {foo: "bar", func: function () {} }; + ok (_.has(obj, "foo"), "has() checks that the object has a property."); + ok (_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property."); + ok (_.has(obj, "func"), "has() works for functions too."); + obj.hasOwnProperty = null; + ok (_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted."); + var child = {}; + child.prototype = obj; + ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") + }); + `) + }) +} diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_test.go new file mode 100644 index 000000000..96c8f2e93 --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_test.go @@ -0,0 +1,165 @@ +package otto + +import ( + "./terst" + "testing" + + "github.com/robertkrimen/otto/underscore" +) + +func init() { + underscore.Disable() +} + +// A persistent handle for the underscore tester +// We do not run underscore tests in parallel, so it is okay to stash globally +// (Maybe use sync.Pool in the future...) +var tester_ *_tester + +// A tester for underscore: test_ => test(underscore) :) +func test_(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { + tester := tester_ + if tester == nil { + tester = newTester() + tester.underscore() // Load underscore and testing shim, etc. + tester_ = tester + } + + return tester.test, tester +} + +func (self *_tester) underscore() { + vm := self.vm + _, err := vm.Run(underscore.Source()) + if err != nil { + panic(err) + } + + vm.Set("assert", func(call FunctionCall) Value { + if !toBoolean(call.Argument(0)) { + message := "Assertion failed" + if len(call.ArgumentList) > 1 { + message = toString(call.ArgumentList[1]) + } + t := terst.Caller().T() + is(message, nil) + t.Fail() + return FalseValue() + } + return TrueValue() + }) + + vm.Run(` + var templateSettings; + + function _setup() { + templateSettings = _.clone(_.templateSettings); + } + + function _teardown() { + _.templateSettings = templateSettings; + } + + function module() { + /* Nothing happens. */ + } + + function equals(a, b, emit) { + assert(a == b, emit + ", <" + a + "> != <" + b + ">"); + } + var equal = equals; + + function notStrictEqual(a, b, emit) { + assert(a !== b, emit); + } + + function strictEqual(a, b, emit) { + assert(a === b, emit); + } + + function ok(a, emit) { + assert(a, emit); + } + + function raises(fn, want, emit) { + var have, _ok = false; + if (typeof want === "string") { + emit = want; + want = null; + } + + try { + fn(); + } catch(tmp) { + have = tmp; + } + + if (have) { + if (!want) { + _ok = true; + } + else if (want instanceof RegExp) { + _ok = want.test(have); + } + else if (have instanceof want) { + _ok = true + } + else if (want.call({}, have) === true) { + _ok = true; + } + } + + ok(_ok, emit); + } + + function test(name){ + _setup() + try { + templateSettings = _.clone(_.templateSettings); + if (arguments.length == 3) { + count = 0 + for (count = 0; count < arguments[1]; count++) { + arguments[2]() + } + } else { + // For now. + arguments[1]() + } + } + finally { + _teardown() + } + } + + function deepEqual(a, b, emit) { + // Also, for now. + assert(_.isEqual(a, b), emit) + } + `) +} + +func Test_underscore(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + _.map([1, 2, 3], function(value){ + return value + 1 + }) + `, "2,3,4") + + test(` + abc = _.find([1, 2, 3, -1], function(value) { return value == -1 }) + `, -1) + + test(`_.isEqual(1, 1)`, true) + test(`_.isEqual([], [])`, true) + test(`_.isEqual(['b', 'd'], ['b', 'd'])`, true) + test(`_.isEqual(['b', 'd', 'c'], ['b', 'd', 'e'])`, false) + test(`_.isFunction(function(){})`, true) + test(`_.template('

\u2028<%= "\\u2028\\u2029" %>\u2029

')()`, "

\u2028\u2028\u2029\u2029

") + }) +} + +// TODO Test: typeof An argument reference +// TODO Test: abc = {}; abc == Object(abc) diff --git a/Godeps/_workspace/src/github.com/obscuren/otto/underscore_utility_test.go b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_utility_test.go new file mode 100644 index 000000000..ebabb083b --- /dev/null +++ b/Godeps/_workspace/src/github.com/obscuren/otto/underscore_utility_test.go @@ -0,0 +1,419 @@ +package otto + +import ( + "testing" +) + +// #750 - Return _ instance. +func Test_underscore_utility_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("#750 - Return _ instance.", 2, function() { + var instance = _([]); + ok(_(instance) === instance); + ok(new _(instance) === instance); + }); + `) + }) +} + +// identity +func Test_underscore_utility_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("identity", function() { + var moe = {name : 'moe'}; + equal(_.identity(moe), moe, 'moe is the same as his identity'); + }); + `) + }) +} + +// random +func Test_underscore_utility_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("random", function() { + var array = _.range(1000); + var min = Math.pow(2, 31); + var max = Math.pow(2, 62); + + ok(_.every(array, function() { + return _.random(min, max) >= min; + }), "should produce a random number greater than or equal to the minimum number"); + + ok(_.some(array, function() { + return _.random(Number.MAX_VALUE) > 0; + }), "should produce a random number when passed "); + }); + `) + }) +} + +// uniqueId +func Test_underscore_utility_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("uniqueId", function() { + var ids = [], i = 0; + while(i++ < 100) ids.push(_.uniqueId()); + equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + }); + `) + }) +} + +// times +func Test_underscore_utility_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("times", function() { + var vals = []; + _.times(3, function (i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); + // + vals = []; + _(3).times(function(i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); + // collects return values + ok(_.isEqual([0, 1, 2], _.times(3, function(i) { return i; })), "collects return values"); + }); + `) + }) +} + +// mixin +func Test_underscore_utility_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("mixin", function() { + _.mixin({ + myReverse: function(string) { + return string.split('').reverse().join(''); + } + }); + equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + }); + `) + }) +} + +// _.escape +func Test_underscore_utility_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("_.escape", function() { + equal(_.escape("Curly & Moe"), "Curly & Moe"); + equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); + equal(_.escape(null), ''); + }); + `) + }) +} + +// _.unescape +func Test_underscore_utility_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("_.unescape", function() { + var string = "Curly & Moe"; + equal(_.unescape("Curly & Moe"), string); + equal(_.unescape("Curly &amp; Moe"), "Curly & Moe"); + equal(_.unescape(null), ''); + equal(_.unescape(_.escape(string)), string); + }); + `) + }) +} + +// template +func Test_underscore_utility_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("template", function() { + var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); + var result = basicTemplate({thing : 'This'}); + equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + + var sansSemicolonTemplate = _.template("A <% this %> B"); + equal(sansSemicolonTemplate(), "A B"); + + var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); + equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); + + var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); + equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); + + var fancyTemplate = _.template("
    <% \ + for (var key in people) { \ + %>
  • <%= people[key] %>
  • <% } %>
"); + result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equal(result, "
  • Moe
  • Larry
  • Curly
", 'can run arbitrary javascript in templates'); + + var escapedCharsInJavascriptTemplate = _.template("
    <% _.each(numbers.split('\\n'), function(item) { %>
  • <%= item %>
  • <% }) %>
"); + result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); + equal(result, "
  • one
  • two
  • three
  • four
", 'Can use escaped characters (e.g. \\n) in Javascript'); + + var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
\">
<% }); %>"); + result = namespaceCollisionTemplate({ + pageCount: 3, + thumbnails: { + 1: "p1-thumbnail.gif", + 2: "p2-thumbnail.gif", + 3: "p3-thumbnail.gif" + } + }); + equal(result, "3 p3-thumbnail.gif
"); + + var noInterpolateTemplate = _.template("

Just some text. Hey, I know this is silly but it aids consistency.

"); + result = noInterpolateTemplate(); + equal(result, "

Just some text. Hey, I know this is silly but it aids consistency.

"); + + var quoteTemplate = _.template("It's its, not it's"); + equal(quoteTemplate({}), "It's its, not it's"); + + var quoteInStatementAndBody = _.template("<%\ + if(foo == 'bar'){ \ + %>Statement quotes and 'quotes'.<% } %>"); + equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + + var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); + equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + + var template = _.template("<%- value %>"); + var result = template({value: " -