Merge branch 'develop' into onboarding

This commit is contained in:
Alexandre Van de Sande 2015-04-06 17:04:47 -03:00
commit 69faadd175
904 changed files with 70447 additions and 21646 deletions

6
.gitignore vendored
View file

@ -13,6 +13,8 @@
.ethtest
*/**/*tx_database*
*/**/*dapps*
Godeps/_workspace/pkg
Godeps/_workspace/bin
#*
.#*
@ -21,7 +23,9 @@
.project
.settings
cmd/ethereum/ethereum
geth
mist
cmd/geth/geth
cmd/mist/mist
deploy/osx/Mist.app
deploy/osx/Mist\ Installer.dmg

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "ethereal/assets/samplecoin"]
path = ethereal/assets/samplecoin
url = git@github.com:obscuren/SampleCoin.git
[submodule "cmd/mist/assets/ext/ethereum.js"]
path = cmd/mist/assets/ext/ethereum.js
url = https://github.com/ethereum/ethereum.js

View file

@ -10,3 +10,5 @@ Joseph Goulden <joegoulden@gmail.com>
Nick Savers <nicksavers@gmail.com>
Maran Hidskes <maran.hidskes@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>

View file

@ -9,7 +9,7 @@ install:
# - go get code.google.com/p/go.tools/cmd/goimports
# - go get github.com/golang/lint/golint
# - go get golang.org/x/tools/cmd/vet
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
before_script:
# - gofmt -l -w .
@ -27,3 +27,10 @@ env:
- LD_LIBRARY_PATH=/opt/qt54/lib
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e09ccdce1048c5e03445
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View file

@ -26,15 +26,14 @@ RUN tar -C /usr/local -xzf go*.tar.gz && go version
ADD https://api.github.com/repos/ethereum/go-ethereum/git/refs/heads/develop file_does_not_exist
## Fetch and install go-ethereum
RUN go get github.com/tools/godep
RUN go get -d github.com/ethereum/go-ethereum/...
RUN mkdir -p $GOPATH/src/github.com/ethereum/
RUN git clone https://github.com/ethereum/go-ethereum $GOPATH/src/github.com/ethereum/go-ethereum
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
RUN git checkout develop
RUN godep restore
RUN go install -v ./cmd/ethereum
RUN GOPATH=$GOPATH:$GOPATH/src/github.com/ethereum/go-ethereum/Godeps/_workspace go install -v ./cmd/geth
## Run & expose JSON RPC
ENTRYPOINT ["ethereum", "-rpc=true", "-rpcport=8545"]
ENTRYPOINT ["geth", "-rpc=true", "-rpcport=8545"]
EXPOSE 8545

16
Godeps/Godeps.json generated
View file

@ -17,13 +17,13 @@
},
{
"ImportPath": "github.com/codegangsta/cli",
"Comment": "1.2.0-81-g3e09053",
"Rev": "3e0905345cd2c5366530dbcdce62457f2ce16e7c"
"Comment": "1.2.0-95-g9b2bd2b",
"Rev": "9b2bd2b3489748d4d0a204fa4eb2ee9e89e0ebc6"
},
{
"ImportPath": "github.com/ethereum/ethash",
"Comment": "v23-12-g149261a",
"Rev": "149261a5d7cafc3943cbcf1d370082ec70d81e8b"
"Comment": "v23.1-73-g67a0e12",
"Rev": "67a0e12a091de035ef083186247e84be2d863c62"
},
{
"ImportPath": "github.com/ethereum/serpent-go",
@ -47,8 +47,8 @@
"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
},
{
"ImportPath": "github.com/obscuren/otto",
"Rev": "cf13cc4228c5e5ce0fe27a7aea90bc10091c4f19"
"ImportPath": "github.com/robertkrimen/otto",
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
},
{
"ImportPath": "github.com/obscuren/qml",
@ -90,6 +90,10 @@
"ImportPath": "github.com/robertkrimen/otto/token",
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
},
{
"ImportPath": "github.com/rs/cors",
"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb"
},
{
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
"Rev": "832fa7ed4d28545eab80f19e1831fc004305cade"

View file

@ -210,7 +210,7 @@ Subcommands can be defined for a more git-like command line app.
app.Commands = []cli.Command{
{
Name: "add",
ShortName: "a",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) {
println("added task: ", c.Args().First())
@ -218,7 +218,7 @@ app.Commands = []cli.Command{
},
{
Name: "complete",
ShortName: "c",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())
@ -226,7 +226,7 @@ app.Commands = []cli.Command{
},
{
Name: "template",
ShortName: "r",
Aliases: []string{"r"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
@ -263,7 +263,7 @@ app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "complete",
ShortName: "c",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())

View file

@ -5,6 +5,7 @@ import (
"io"
"io/ioutil"
"os"
"strings"
"text/tabwriter"
"text/template"
"time"
@ -72,17 +73,14 @@ func NewApp() *App {
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
Author: "Dr. James",
Email: "who@gmail.com",
Authors: []Author{{"Jim", "jim@corporate.com"}, {"Hank", "hank@indiepalace.com"}},
Writer: os.Stdout,
}
}
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
if a.Author != "" && a.Author != "" {
a.Authors = append(a.Authors, Author{a.Author, a.Email})
if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
}
if HelpPrinter == nil {
@ -91,8 +89,12 @@ func (a *App) Run(arguments []string) (err error) {
}()
HelpPrinter = func(templ string, data interface{}) {
funcMap := template.FuncMap{
"join": strings.Join,
}
w := tabwriter.NewWriter(a.Writer, 0, 8, 1, '\t', 0)
t := template.Must(template.New("help").Parse(templ))
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
err := t.Execute(w, data)
if err != nil {
panic(err)

View file

@ -23,7 +23,7 @@ func ExampleApp() {
}
app.Author = "Harrison"
app.Email = "harrison@lolwut.com"
app.Authors = []cli.Author{{"Oliver Allen", "oliver@toyshop.com"}}
app.Authors = []cli.Author{cli.Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
app.Run(os.Args)
// Output:
// Hello Jeremy
@ -37,13 +37,13 @@ func ExampleAppSubcommand() {
app.Commands = []cli.Command{
{
Name: "hello",
ShortName: "hi",
Aliases: []string{"hi"},
Usage: "use it to see a description",
Description: "This is how we describe hello the function",
Subcommands: []cli.Command{
{
Name: "english",
ShortName: "en",
Aliases: []string{"en"},
Usage: "sends a greeting in english",
Description: "greets someone in english",
Flags: []cli.Flag{
@ -78,7 +78,7 @@ func ExampleAppHelp() {
app.Commands = []cli.Command{
{
Name: "describeit",
ShortName: "d",
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
Action: func(c *cli.Context) {
@ -108,7 +108,7 @@ func ExampleAppBashComplete() {
app.Commands = []cli.Command{
{
Name: "describeit",
ShortName: "d",
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
Action: func(c *cli.Context) {
@ -162,8 +162,8 @@ var commandAppTests = []struct {
func TestApp_Command(t *testing.T) {
app := cli.NewApp()
fooCommand := cli.Command{Name: "foobar", ShortName: "f"}
batCommand := cli.Command{Name: "batbaz", ShortName: "b"}
fooCommand := cli.Command{Name: "foobar", Aliases: []string{"f"}}
batCommand := cli.Command{Name: "batbaz", Aliases: []string{"b"}}
app.Commands = []cli.Command{
fooCommand,
batCommand,

View file

@ -13,7 +13,7 @@ func Example() {
app.Commands = []cli.Command{
{
Name: "add",
ShortName: "a",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) {
println("added task: ", c.Args().First())
@ -21,7 +21,7 @@ func Example() {
},
{
Name: "complete",
ShortName: "c",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())
@ -38,13 +38,13 @@ func ExampleSubcommand() {
app.Commands = []cli.Command{
{
Name: "hello",
ShortName: "hi",
Aliases: []string{"hi"},
Usage: "use it to see a description",
Description: "This is how we describe hello the function",
Subcommands: []cli.Command{
{
Name: "english",
ShortName: "en",
Aliases: []string{"en"},
Usage: "sends a greeting in english",
Description: "greets someone in english",
Flags: []cli.Flag{
@ -59,7 +59,7 @@ func ExampleSubcommand() {
},
}, {
Name: "spanish",
ShortName: "sp",
Aliases: []string{"sp"},
Usage: "sends a greeting in spanish",
Flags: []cli.Flag{
cli.StringFlag{
@ -73,7 +73,7 @@ func ExampleSubcommand() {
},
}, {
Name: "french",
ShortName: "fr",
Aliases: []string{"fr"},
Usage: "sends a greeting in french",
Flags: []cli.Flag{
cli.StringFlag{

View file

@ -10,8 +10,10 @@ import (
type Command struct {
// The name of the command
Name string
// short name of the command. Typically one character
// short name of the command. Typically one character (deprecated, use `Aliases`)
ShortName string
// A list of aliases for the command
Aliases []string
// A short description of the usage of this command
Usage string
// A longer explanation of how the command works
@ -117,9 +119,24 @@ func (c Command) Run(ctx *Context) error {
return nil
}
func (c Command) Names() []string {
names := []string{c.Name}
if c.ShortName != "" {
names = append(names, c.ShortName)
}
return append(names, c.Aliases...)
}
// Returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
return c.Name == name || (c.ShortName != "" && c.ShortName == name)
for _, n := range c.Names() {
if n == name {
return true
}
}
return false
}
func (c Command) startApp(ctx *Context) error {

View file

@ -17,7 +17,7 @@ func TestCommandDoNotIgnoreFlags(t *testing.T) {
command := cli.Command{
Name: "test-cmd",
ShortName: "tc",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(_ *cli.Context) {},
@ -37,7 +37,7 @@ func TestCommandIgnoreFlags(t *testing.T) {
command := cli.Command{
Name: "test-cmd",
ShortName: "tc",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(_ *cli.Context) {},

View file

@ -106,6 +106,11 @@ func (c *Context) GlobalGeneric(name string) interface{} {
return lookupGeneric(name, c.globalSet)
}
// Returns the number of flags set
func (c *Context) NumFlags() int {
return c.flagSet.NFlag()
}
// Determines if the flag was actually set
func (c *Context) IsSet(name string) bool {
if c.setFlags == nil {

View file

@ -97,3 +97,15 @@ func TestContext_GlobalIsSet(t *testing.T) {
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
expect(t, c.GlobalIsSet("bogusGlobal"), false)
}
func TestContext_NumFlags(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc")
c := cli.NewContext(nil, set, globalSet)
set.Parse([]string{"--myflag", "--otherflag=foo"})
globalSet.Parse([]string{"--myflagGlobal"})
expect(t, c.NumFlags(), 2)
}

View file

@ -15,10 +15,10 @@ VERSION:
{{.Version}}
AUTHOR(S):
{{range .Authors}}{{ . }} {{end}}
{{range .Authors}}{{ . }}
{{end}}
COMMANDS:
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
GLOBAL OPTIONS:
{{range .Flags}}{{.}}
@ -52,7 +52,7 @@ USAGE:
{{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...]
COMMANDS:
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
OPTIONS:
{{range .Flags}}{{.}}
@ -61,7 +61,7 @@ OPTIONS:
var helpCommand = Command{
Name: "help",
ShortName: "h",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
Action: func(c *Context) {
args := c.Args()
@ -75,7 +75,7 @@ var helpCommand = Command{
var helpSubcommand = Command{
Name: "help",
ShortName: "h",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
Action: func(c *Context) {
args := c.Args()
@ -102,9 +102,8 @@ func ShowAppHelp(c *Context) {
// Prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
for _, command := range c.App.Commands {
fmt.Fprintln(c.App.Writer, command.Name)
if command.ShortName != "" {
fmt.Fprintln(c.App.Writer, command.ShortName)
for _, name := range command.Names() {
fmt.Fprintln(c.App.Writer, name)
}
}
}

View file

@ -1,6 +1,14 @@
# making our travis.yml play well with C++11 by obtaining g++4.8
# Taken from this file:
# https://github.com/beark/ftl/blob/master/.travis.yml
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq wget cmake gcc bash libboost-test-dev nodejs python-pip python-dev
- sudo pip install virtualenv -q
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -y -qq
install:
- sudo apt-get install -qq --yes --force-yes g++-4.8
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
# need to explicitly request version 1.48 since by default we get 1.46 which does not work with C++11
- sudo apt-get install -qq wget cmake bash libboost-test1.48-dev libboost-system1.48-dev libboost-filesystem1.48-dev nodejs python-pip python-dev
- sudo pip install virtualenv -q
script: "./test/test.sh"

View file

@ -18,4 +18,4 @@ if (OpenCL_FOUND)
add_subdirectory(src/libethash-cl)
endif()
add_subdirectory(src/benchmark EXCLUDE_FROM_ALL)
add_subdirectory(test/c EXCLUDE_FROM_ALL)
add_subdirectory(test/c)

View file

@ -118,7 +118,19 @@ if(WIN32)
endif()
else()
find_library(OpenCL_LIBRARY
NAMES OpenCL)
NAMES OpenCL
PATHS
ENV "PROGRAMFILES(X86)"
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV CUDA_PATH
ENV NVSDKCOMPUTE_ROOT
ENV ATISTREAMSDKROOT
PATH_SUFFIXES
"AMD APP/lib/x86_64"
lib/x86_64
lib/x64
OpenCL/common/lib/x64)
endif()
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})

View file

@ -31,8 +31,8 @@ import (
"time"
"unsafe"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/pow"
)
@ -85,7 +85,7 @@ func makeParamsAndCache(chainManager pow.ChainManager, blockNum uint64) (*Params
Epoch: blockNum / epochLength,
}
C.ethash_params_init(paramsAndCache.params, C.uint32_t(uint32(blockNum)))
paramsAndCache.cache.mem = C.malloc(paramsAndCache.params.cache_size)
paramsAndCache.cache.mem = C.malloc(C.size_t(paramsAndCache.params.cache_size))
seedHash, err := GetSeedHash(blockNum)
if err != nil {
@ -100,14 +100,14 @@ func makeParamsAndCache(chainManager pow.ChainManager, blockNum uint64) (*Params
return paramsAndCache, nil
}
func (pow *Ethash) UpdateCache(force bool) error {
func (pow *Ethash) UpdateCache(blockNum uint64, force bool) error {
pow.cacheMutex.Lock()
defer pow.cacheMutex.Unlock()
thisEpoch := pow.chainManager.CurrentBlock().NumberU64() / epochLength
thisEpoch := blockNum / epochLength
if force || pow.paramsAndCache.Epoch != thisEpoch {
var err error
pow.paramsAndCache, err = makeParamsAndCache(pow.chainManager, pow.chainManager.CurrentBlock().NumberU64())
pow.paramsAndCache, err = makeParamsAndCache(pow.chainManager, blockNum)
if err != nil {
panic(err)
}
@ -118,7 +118,7 @@ func (pow *Ethash) UpdateCache(force bool) error {
func makeDAG(p *ParamsAndCache) *DAG {
d := &DAG{
dag: C.malloc(p.params.full_size),
dag: C.malloc(C.size_t(p.params.full_size)),
file: false,
paramsAndCache: p,
}
@ -326,7 +326,6 @@ func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte
for {
select {
case <-stop:
powlogger.Infoln("Breaking from mining")
pow.HashRate = 0
return 0, nil, nil
default:
@ -364,7 +363,7 @@ func (pow *Ethash) Verify(block pow.Block) bool {
return pow.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), block.Nonce())
}
func (pow *Ethash) verify(hash []byte, mixDigest []byte, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
func (pow *Ethash) verify(hash common.Hash, mixDigest common.Hash, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
// Make sure the block num is valid
if blockNum >= epochLength*2048 {
powlogger.Infoln(fmt.Sprintf("Block number exceeds limit, invalid (value is %v, limit is %v)",
@ -386,13 +385,13 @@ func (pow *Ethash) verify(hash []byte, mixDigest []byte, difficulty *big.Int, bl
if blockNum/epochLength < pow.paramsAndCache.Epoch {
var err error
// If we can't make the params for some reason, this block is invalid
pAc, err = makeParamsAndCache(pow.chainManager, blockNum)
pAc, err = makeParamsAndCache(pow.chainManager, blockNum+1)
if err != nil {
powlogger.Infoln(err)
powlogger.Infoln("big fucking eror", err)
return false
}
} else {
pow.UpdateCache(false)
pow.UpdateCache(blockNum, false)
pow.cacheMutex.RLock()
defer pow.cacheMutex.RUnlock()
pAc = pow.paramsAndCache

View file

@ -6,6 +6,11 @@ if (MSVC)
add_definitions("/openmp")
endif()
# enable C++11, should probably be a bit more specific about compiler
if (NOT MSVC)
SET(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if (NOT MPI_FOUND)
find_package(MPI)
endif()

View file

@ -21,7 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <chrono>
#include <libethash/ethash.h>
#include <libethash/util.h>
#ifdef OPENCL
@ -41,6 +41,8 @@
#undef min
#undef max
using std::chrono::high_resolution_clock;
#if defined(OPENCL)
const unsigned trials = 1024*1024*32;
#elif defined(FULL)
@ -122,50 +124,50 @@ extern "C" int main(void)
// compute cache or full data
{
clock_t startTime = clock();
auto startTime = high_resolution_clock::now();
ethash_mkcache(&cache, &params, seed);
clock_t time = clock() - startTime;
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
uint8_t cache_hash[32];
SHA3_256(cache_hash, (uint8_t const*)cache_mem, params.cache_size);
debugf("ethash_mkcache: %ums, sha3: %s\n", (unsigned)((time*1000)/CLOCKS_PER_SEC), bytesToHexString(cache_hash,sizeof(cache_hash)).data());
debugf("ethash_mkcache: %ums, sha3: %s\n", (unsigned)time, bytesToHexString(cache_hash,sizeof(cache_hash)).data());
// print a couple of test hashes
{
const clock_t startTime = clock();
auto startTime = high_resolution_clock::now();
ethash_return_value hash;
ethash_light(&hash, &cache, &params, previous_hash, 0);
const clock_t time = clock() - startTime;
debugf("ethash_light test: %ums, %s\n", (unsigned)((time*1000)/CLOCKS_PER_SEC), bytesToHexString(hash.result, 32).data());
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_light test: %ums, %s\n", (unsigned)time, bytesToHexString(hash.result, 32).data());
}
#ifdef FULL
startTime = clock();
startTime = high_resolution_clock::now();
ethash_compute_full_data(full_mem, &params, &cache);
time = clock() - startTime;
debugf("ethash_compute_full_data: %ums\n", (unsigned)((time*1000)/CLOCKS_PER_SEC));
time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_compute_full_data: %ums\n", (unsigned)time);
#endif // FULL
}
#ifdef OPENCL
ethash_cl_miner miner;
{
const clock_t startTime = clock();
auto startTime = high_resolution_clock::now();
if (!miner.init(params, seed))
exit(-1);
const clock_t time = clock() - startTime;
debugf("ethash_cl_miner init: %ums\n", (unsigned)((time*1000)/CLOCKS_PER_SEC));
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_cl_miner init: %ums\n", (unsigned)time);
}
#endif
#ifdef FULL
{
const clock_t startTime = clock();
auto startTime = high_resolution_clock::now();
ethash_return_value hash;
ethash_full(&hash, full_mem, &params, previous_hash, 0);
const clock_t time = clock() - startTime;
debugf("ethash_full test: %uns, %s\n", (unsigned)((time*1000000)/CLOCKS_PER_SEC), bytesToHexString(hash.result, 32).data());
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now() - startTime).count();
debugf("ethash_full test: %uns, %s\n", (unsigned)time);
}
#endif
@ -186,10 +188,12 @@ extern "C" int main(void)
}
}
}
// ensure nothing else is going on
miner.finish();
#endif
clock_t startTime = clock();
auto startTime = high_resolution_clock::now();
unsigned hash_count = trials;
#ifdef OPENCL
@ -201,7 +205,7 @@ extern "C" int main(void)
virtual bool found(uint64_t const* nonces, uint32_t count)
{
nonce_vec.assign(nonces, nonces + count);
nonce_vec.insert(nonce_vec.end(), nonces, nonces + count);
return false;
}
@ -241,15 +245,23 @@ extern "C" int main(void)
}
}
#endif
clock_t time = std::max((clock_t)1u, clock() - startTime);
auto time = std::chrono::duration_cast<std::chrono::microseconds>(high_resolution_clock::now() - startTime).count();
debugf("Search took: %ums\n", (unsigned)time/1000);
unsigned read_size = ACCESSES * MIX_BYTES;
#if defined(OPENCL) || defined(FULL)
debugf(
"hashrate: %8u, bw: %6u MB/s\n",
(unsigned)(((uint64_t)hash_count*CLOCKS_PER_SEC)/time),
(unsigned)((((uint64_t)hash_count*read_size*CLOCKS_PER_SEC)/time) / (1024*1024))
"hashrate: %8.2f Mh/s, bw: %8.2f GB/s\n",
(double)hash_count * (1000*1000)/time / (1000*1000),
(double)hash_count*read_size * (1000*1000)/time / (1024*1024*1024)
);
#else
debugf(
"hashrate: %8.2f Kh/s, bw: %8.2f MB/s\n",
(double)hash_count * (1000*1000)/time / (1000),
(double)hash_count*read_size * (1000*1000)/time / (1024*1024)
);
#endif
free(cache_mem_buf);
#ifdef FULL

View file

@ -1,3 +1,5 @@
cmake_minimum_required(VERSION 2.8)
set(LIBRARY ethash-cl)
set(CMAKE_BUILD_TYPE Release)
@ -28,11 +30,16 @@ if (NOT MSVC)
endif ()
endif()
set(OpenCL_FOUND TRUE)
set(OpenCL_INCLUDE_DIRS /usr/include/CL)
set(OpenCL_LIBRARIES -lOpenCL)
if (NOT OpenCL_FOUND)
find_package(OpenCL)
endif()
if (OpenCL_FOUND)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -Werror -pedantic -fPIC ${CMAKE_CXX_FLAGS}")
include_directories(${OpenCL_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
include_directories(..)
add_library(${LIBRARY} ethash_cl_miner.cpp ethash_cl_miner.h cl.hpp)

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,8 @@
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstdlib>
#include <assert.h>
#include <queue>
#include <vector>
@ -52,6 +54,14 @@ ethash_cl_miner::ethash_cl_miner()
{
}
void ethash_cl_miner::finish()
{
if (m_queue())
{
m_queue.finish();
}
}
bool ethash_cl_miner::init(ethash_params const& params, const uint8_t seed[32], unsigned workgroup_size)
{
// store params
@ -250,11 +260,10 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event);
}
else
#else
#endif
{
m_queue.finish();
}
#endif
/*
__kernel void ethash_combined_search(
@ -309,6 +318,10 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
if (exit)
break;
// reset search buffer if we're still going
if (num_found)
m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero);
pending.pop();
}
}

View file

@ -21,6 +21,7 @@ public:
bool init(ethash_params const& params, const uint8_t seed[32], unsigned workgroup_size = 64);
void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
void search(uint8_t const* header, uint64_t target, search_hook& hook);

View file

@ -12,6 +12,7 @@ endif()
set(FILES util.c
util.h
io.c
internal.c
ethash.h
endian.h
@ -19,6 +20,12 @@ set(FILES util.c
fnv.h
data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()
if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2)
endif()

View file

@ -48,7 +48,7 @@ extern "C" {
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
static const size_t dag_sizes[2048] = {
static const uint64_t dag_sizes[2048] = {
1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U,
1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U,
1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U,
@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = {
// While[! PrimeQ[i], i--];
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
const size_t cache_sizes[2048] = {
const uint64_t cache_sizes[2048] = {
16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U,
17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U,
18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U,

View file

@ -43,8 +43,8 @@ extern "C" {
#endif
typedef struct ethash_params {
size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
typedef struct ethash_return_value {
@ -52,8 +52,8 @@ typedef struct ethash_return_value {
uint8_t mix_hash[32];
} ethash_return_value;
size_t ethash_get_datasize(const uint32_t block_number);
size_t ethash_get_cachesize(const uint32_t block_number);
uint64_t ethash_get_datasize(const uint32_t block_number);
uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
@ -93,23 +93,30 @@ static inline void ethash_compute_full(ethash_return_value *ret, void const *ful
ethash_full(ret, full, params, header_hash, nonce);
}
// Returns if hash is less than or equal to difficulty
static inline int ethash_check_difficulty(
const uint8_t hash[32],
const uint8_t difficulty[32]) {
// Difficulty is big endian
/// @brief Compare two s256-bit big-endian values.
/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise.
/// Both parameters are 256-bit big-endian values.
static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) {
// Boundary is big endian
for (int i = 0; i < 32; i++) {
if (hash[i] == difficulty[i]) continue;
return hash[i] < difficulty[i];
if (a[i] == b[i])
continue;
return a[i] < b[i];
}
return 1;
}
int ethash_quick_check_difficulty(
/// Perofrms a cursory check on the validity of the nonce.
/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary.
/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian.
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t difficulty[32]);
const uint8_t boundary[32]);
#define ethash_quick_check_difficulty ethash_preliminary_check_boundary
#define ethash_check_difficulty ethash_leq_be256
#ifdef __cplusplus
}

View file

@ -37,12 +37,12 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
size_t ethash_get_datasize(const uint32_t block_number) {
uint64_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH];
}
size_t ethash_get_cachesize(const uint32_t block_number) {
uint64_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH];
}
@ -280,7 +280,7 @@ void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
SHA3_256(seedhash, seedhash, 32);
}
int ethash_quick_check_difficulty(
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
@ -288,7 +288,7 @@ int ethash_quick_check_difficulty(
uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(return_hash, difficulty);
return ethash_leq_be256(return_hash, difficulty);
}
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {

View file

@ -0,0 +1,89 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <string.h>
#include <stdio.h>
// silly macro to save some typing
#define PASS_ARR(c_) (c_), sizeof(c_)
static bool ethash_io_write_file(char const *dirname,
char const* filename,
size_t filename_length,
void const* data,
size_t data_size)
{
bool ret = false;
char *fullname = ethash_io_create_filename(dirname, filename, filename_length);
if (!fullname) {
return false;
}
FILE *f = fopen(fullname, "wb");
if (!f) {
goto free_name;
}
if (data_size != fwrite(data, 1, data_size, f)) {
goto close;
}
ret = true;
close:
fclose(f);
free_name:
free(fullname);
return ret;
}
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
uint64_t *data_size)
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc((size_t)params->full_size);
if (!temp_data_ptr) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) {
goto fail_free;
}
ethash_io_serialize_info(REVISION, seedhash, info_buffer);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) {
goto fail_free;
}
*data = temp_data_ptr;
*data_size = params->full_size;
return true;
fail_free:
free(temp_data_ptr);
end:
return false;
}
#undef PASS_ARR

View file

@ -0,0 +1,116 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "ethash.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t;
static const char DAG_FILE_NAME[] = "full";
static const char DAG_MEMO_NAME[] = "full.info";
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :(
#define DAG_MEMO_BYTESIZE 36
/// Possible return values of @see ethash_io_prepare
enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch
ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything
};
/**
* Prepares io for ethash
*
* Create the DAG directory if it does not exist, and check if the memo file matches.
* If it does not match then it's deleted to pave the way for @ref ethash_io_write()
*
* @param dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created.
* @param seedhash The seedhash of the current block number
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
/**
* Fully computes data and writes it to the file on disk.
*
* This function should be called after @see ethash_io_prepare() and only if
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data
* and the memo file.
*
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. Has to exist.
* @param[in] params An ethash_params object containing the full size
* and the cache size
* @param[in] seedhash The seedhash of the current block number
* @param[in] cache The cache data. Would have usually been calulated by
* @see ethash_prep_light().
* @param[out] data Pass a pointer to uint8_t by reference here. If the
* function is succesfull then this point to the allocated
* data calculated by @see ethash_prep_full(). Memory
* ownership is transfered to the callee. Remember that
* you eventually need to free this with a call to free().
* @param[out] data_size Pass a uint64_t by value. If the function is succesfull
* then this will contain the number of bytes allocated
* for @a data.
* @return True for success and false in case of failure.
*/
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
uint64_t *data_size);
static inline void ethash_io_serialize_info(uint32_t revision,
ethash_blockhash_t seed_hash,
char *output)
{
// if .info is only consumed locally we don't really care about endianess
memcpy(output, &revision, 4);
memcpy(output + 4, &seed_hash, 32);
}
static inline char *ethash_io_create_filename(char const *dirname,
char const* filename,
size_t filename_length)
{
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char *name = (char*)malloc(strlen(dirname) + filename_length);
if (!name) {
return NULL;
}
name[0] = '\0';
strcat(name, dirname);
strcat(name, filename);
return name;
}
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,76 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_posix.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists, full owner permissions and read/search for others
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

View file

@ -0,0 +1,73 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_win32.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <direct.h>
#include <errno.h>
#include <stdio.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists
int rc = _mkdir(dirname);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (_unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

View file

@ -5,6 +5,14 @@
#include <time.h>
#include "../libethash/ethash.h"
#if PY_MAJOR_VERSION >= 3
#define PY_STRING_FORMAT "y#"
#define PY_CONST_STRING_FORMAT "y"
#else
#define PY_STRING_FORMAT "s#"
#define PY_CONST_STRING_FORMAT "s"
#endif
#define MIX_WORDS (MIX_BYTES/4)
static PyObject *
@ -46,7 +54,7 @@ mkcache_bytes(PyObject *self, PyObject *args) {
unsigned long cache_size;
int seed_len;
if (!PyArg_ParseTuple(args, "ks#", &cache_size, &seed, &seed_len))
if (!PyArg_ParseTuple(args, "k" PY_STRING_FORMAT, &cache_size, &seed, &seed_len))
return 0;
if (seed_len != 32) {
@ -62,7 +70,7 @@ mkcache_bytes(PyObject *self, PyObject *args) {
ethash_cache cache;
cache.mem = malloc(cache_size);
ethash_mkcache(&cache, &params, (uint8_t *) seed);
PyObject * val = Py_BuildValue("s#", cache.mem, cache_size);
PyObject * val = Py_BuildValue(PY_STRING_FORMAT, cache.mem, cache_size);
free(cache.mem);
return val;
}
@ -74,7 +82,7 @@ calc_dataset_bytes(PyObject *self, PyObject *args) {
unsigned long full_size;
int cache_size;
if (!PyArg_ParseTuple(args, "ks#", &full_size, &cache_bytes, &cache_size))
if (!PyArg_ParseTuple(args, "k" PY_STRING_FORMAT, &full_size, &cache_bytes, &cache_size))
return 0;
if (full_size % MIX_WORDS != 0) {
@ -98,7 +106,7 @@ calc_dataset_bytes(PyObject *self, PyObject *args) {
cache.mem = (void *) cache_bytes;
void *mem = malloc(params.full_size);
ethash_compute_full_data(mem, &params, &cache);
PyObject * val = Py_BuildValue("s#", (char *) mem, full_size);
PyObject * val = Py_BuildValue(PY_STRING_FORMAT, (char *) mem, full_size);
free(mem);
return val;
}
@ -111,7 +119,7 @@ hashimoto_light(PyObject *self, PyObject *args) {
unsigned long long nonce;
int cache_size, header_size;
if (!PyArg_ParseTuple(args, "ks#s#K", &full_size, &cache_bytes, &cache_size, &header, &header_size, &nonce))
if (!PyArg_ParseTuple(args, "k" PY_STRING_FORMAT PY_STRING_FORMAT "K", &full_size, &cache_bytes, &cache_size, &header, &header_size, &nonce))
return 0;
if (full_size % MIX_WORDS != 0) {
@ -143,7 +151,7 @@ hashimoto_light(PyObject *self, PyObject *args) {
ethash_cache cache;
cache.mem = (void *) cache_bytes;
ethash_light(&out, &cache, &params, (uint8_t *) header, nonce);
return Py_BuildValue("{s:s#,s:s#}",
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "," PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "}",
"mix digest", out.mix_hash, 32,
"result", out.result, 32);
}
@ -155,7 +163,7 @@ hashimoto_full(PyObject *self, PyObject *args) {
unsigned long long nonce;
int full_size, header_size;
if (!PyArg_ParseTuple(args, "s#s#K", &full_bytes, &full_size, &header, &header_size, &nonce))
if (!PyArg_ParseTuple(args, PY_STRING_FORMAT PY_STRING_FORMAT "K", &full_bytes, &full_size, &header, &header_size, &nonce))
return 0;
if (full_size % MIX_WORDS != 0) {
@ -177,7 +185,7 @@ hashimoto_full(PyObject *self, PyObject *args) {
ethash_params params;
params.full_size = (size_t) full_size;
ethash_full(&out, (void *) full_bytes, &params, (uint8_t *) header, nonce);
return Py_BuildValue("{s:s#, s:s#}",
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT "}",
"mix digest", out.mix_hash, 32,
"result", out.result, 32);
}
@ -190,7 +198,7 @@ mine(PyObject *self, PyObject *args) {
uint64_t nonce = ((uint64_t) rand()) << 32 | rand();
int full_size, header_size, difficulty_size;
if (!PyArg_ParseTuple(args, "s#s#s#", &full_bytes, &full_size, &header, &header_size, &difficulty, &difficulty_size))
if (!PyArg_ParseTuple(args, PY_STRING_FORMAT PY_STRING_FORMAT PY_STRING_FORMAT, &full_bytes, &full_size, &header, &header_size, &difficulty, &difficulty_size))
return 0;
if (full_size % MIX_WORDS != 0) {
@ -224,7 +232,7 @@ mine(PyObject *self, PyObject *args) {
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
} while (!ethash_check_difficulty(out.result, (const uint8_t *) difficulty));
return Py_BuildValue("{s:s#, s:s#, s:K}",
return Py_BuildValue("{" PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":" PY_STRING_FORMAT ", " PY_CONST_STRING_FORMAT ":K}",
"mix digest", out.mix_hash, 32,
"result", out.result, 32,
"nonce", nonce);
@ -245,7 +253,7 @@ get_seedhash(PyObject *self, PyObject *args) {
}
uint8_t seedhash[32];
ethash_get_seedhash(seedhash, block_number);
return Py_BuildValue("s#", (char *) seedhash, 32);
return Py_BuildValue(PY_STRING_FORMAT, (char *) seedhash, 32);
}
static PyMethodDef PyethashMethods[] =
@ -287,6 +295,32 @@ static PyMethodDef PyethashMethods[] =
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef PyethashModule = {
PyModuleDef_HEAD_INIT,
"pyethash",
"...",
-1,
PyethashMethods
};
PyMODINIT_FUNC PyInit_pyethash(void) {
PyObject *module = PyModule_Create(&PyethashModule);
// Following Spec: https://github.com/ethereum/wiki/wiki/Ethash#definitions
PyModule_AddIntConstant(module, "REVISION", (long) REVISION);
PyModule_AddIntConstant(module, "DATASET_BYTES_INIT", (long) DATASET_BYTES_INIT);
PyModule_AddIntConstant(module, "DATASET_BYTES_GROWTH", (long) DATASET_BYTES_GROWTH);
PyModule_AddIntConstant(module, "CACHE_BYTES_INIT", (long) CACHE_BYTES_INIT);
PyModule_AddIntConstant(module, "CACHE_BYTES_GROWTH", (long) CACHE_BYTES_GROWTH);
PyModule_AddIntConstant(module, "EPOCH_LENGTH", (long) EPOCH_LENGTH);
PyModule_AddIntConstant(module, "MIX_BYTES", (long) MIX_BYTES);
PyModule_AddIntConstant(module, "HASH_BYTES", (long) HASH_BYTES);
PyModule_AddIntConstant(module, "DATASET_PARENTS", (long) DATASET_PARENTS);
PyModule_AddIntConstant(module, "CACHE_ROUNDS", (long) CACHE_ROUNDS);
PyModule_AddIntConstant(module, "ACCESSES", (long) ACCESSES);
return module;
}
#else
PyMODINIT_FUNC
initpyethash(void) {
PyObject *module = Py_InitModule("pyethash", PyethashMethods);
@ -303,3 +337,4 @@ initpyethash(void) {
PyModule_AddIntConstant(module, "CACHE_ROUNDS", (long) CACHE_ROUNDS);
PyModule_AddIntConstant(module, "ACCESSES", (long) ACCESSES);
}
#endif

View file

@ -1,5 +1,30 @@
if (MSVC)
if (NOT BOOST_ROOT)
set (BOOST_ROOT "$ENV{BOOST_ROOT}")
endif()
set (CMAKE_PREFIX_PATH BOOST_ROOT)
endif()
IF( NOT Boost_FOUND )
find_package(Boost COMPONENTS unit_test_framework)
# use multithreaded boost libraries, with -mt suffix
set(Boost_USE_MULTITHREADED ON)
if (MSVC)
# TODO handle other msvc versions or it will fail find them
set(Boost_COMPILER -vc120)
# use static boost libraries *.lib
set(Boost_USE_STATIC_LIBS ON)
elseif (APPLE)
# use static boost libraries *.a
set(Boost_USE_STATIC_LIBS ON)
elseif (UNIX)
# use dynamic boost libraries .dll
set(Boost_USE_STATIC_LIBS OFF)
endif()
find_package(Boost 1.48.0 COMPONENTS unit_test_framework system filesystem)
ENDIF()
IF( Boost_FOUND )
@ -8,8 +33,9 @@ IF( Boost_FOUND )
link_directories ( ${Boost_LIBRARY_DIRS} )
file(GLOB HEADERS "*.h")
if (NOT MSVC)
ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK)
endif()
if (NOT CRYPTOPP_FOUND)
find_package (CryptoPP)
endif()
@ -18,8 +44,15 @@ IF( Boost_FOUND )
add_definitions(-DWITH_CRYPTOPP)
endif()
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")
endif()
add_executable (Test test.cpp ${HEADERS})
target_link_libraries (Test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${ETHHASH_LIBS})
target_link_libraries(Test ${ETHHASH_LIBS})
target_link_libraries(Test ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(Test ${Boost_SYSTEM_LIBRARIES})
target_link_libraries (Test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
if (CRYPTOPP_FOUND)
TARGET_LINK_LIBRARIES(Test ${CRYPTOPP_LIBRARIES})

View file

@ -2,6 +2,7 @@
#include <libethash/fnv.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>
#include <libethash/io.h>
#ifdef WITH_CRYPTOPP
@ -14,13 +15,23 @@
#define BOOST_TEST_MODULE Daggerhashimoto
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
std::string bytesToHexString(const uint8_t *str, const size_t s) {
using namespace std;
namespace fs = boost::filesystem;
// Just an alloca "wrapper" to silence uint64_t to size_t conversion warnings in windows
// consider replacing alloca calls with something better though!
#define our_alloca(param__) alloca((size_t)(param__))
std::string bytesToHexString(const uint8_t *str, const uint64_t s) {
std::ostringstream ret;
for (int i = 0; i < s; ++i)
for (size_t i = 0; i < s; ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << std::nouppercase << (int) str[i];
return ret.str();
@ -108,9 +119,9 @@ BOOST_AUTO_TEST_CASE(light_and_full_client_checks) {
params.cache_size = 1024;
params.full_size = 1024 * 32;
ethash_cache cache;
cache.mem = alloca(params.cache_size);
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, seed);
node *full_mem = (node *) alloca(params.full_size);
node *full_mem = (node *) our_alloca(params.full_size);
ethash_compute_full_data(full_mem, &params, &cache);
{
@ -226,3 +237,146 @@ BOOST_AUTO_TEST_CASE(ethash_check_difficulty_check) {
!ethash_check_difficulty(hash, target),
"\nexpected \"" << hash << "\" to have more difficulty than \"" << target << "\"\n");
}
BOOST_AUTO_TEST_CASE(test_ethash_dir_creation) {
ethash_blockhash_t seedhash;
memset(&seedhash, 0, 32);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
// cleanup
fs::remove_all("./test_ethash_directory/");
}
BOOST_AUTO_TEST_CASE(test_ethash_io_write_files_are_created) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash((uint8_t*)&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, (uint8_t*)&seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
}
BOOST_AUTO_TEST_CASE(test_ethash_io_memo_file_match) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash((uint8_t*)&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, (uint8_t*)&seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
}
// could have used dev::contentsNew but don't wanna try to import
// libdevcore just for one function
static std::vector<char> readFileIntoVector(char const* filename)
{
ifstream ifs(filename, ios::binary|ios::ate);
ifstream::pos_type pos = ifs.tellg();
std::vector<char> result((unsigned int)pos);
ifs.seekg(0, ios::beg);
ifs.read(&result[0], pos);
return result;
}
BOOST_AUTO_TEST_CASE(test_ethash_io_memo_file_contents) {
ethash_blockhash_t seedhash;
static const int blockn = 0;
ethash_get_seedhash((uint8_t*)&seedhash, blockn);
BOOST_REQUIRE_EQUAL(
ETHASH_IO_MEMO_MISMATCH,
ethash_io_prepare("./test_ethash_directory/", seedhash)
);
// let's make sure that the directory was created
BOOST_REQUIRE(fs::is_directory(fs::path("./test_ethash_directory/")));
ethash_cache cache;
ethash_params params;
uint8_t *data;
uint64_t size;
ethash_params_init(&params, blockn);
params.cache_size = 1024;
params.full_size = 1024 * 32;
cache.mem = our_alloca(params.cache_size);
ethash_mkcache(&cache, &params, (uint8_t*)&seedhash);
BOOST_REQUIRE(
ethash_io_write("./test_ethash_directory/", &params, seedhash, &cache, &data, &size)
);
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full")));
BOOST_REQUIRE(fs::exists(fs::path("./test_ethash_directory/full.info")));
char expect_buffer[DAG_MEMO_BYTESIZE];
ethash_io_serialize_info(REVISION, seedhash, expect_buffer);
auto vec = readFileIntoVector("./test_ethash_directory/full.info");
BOOST_REQUIRE_EQUAL(vec.size(), DAG_MEMO_BYTESIZE);
BOOST_REQUIRE(memcmp(expect_buffer, &vec[0], DAG_MEMO_BYTESIZE) == 0);
// cleanup
fs::remove_all("./test_ethash_directory/");
free(data);
}

View file

@ -1,20 +0,0 @@
#!/bin/bash
# Strict mode
set -e
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
TEST_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
export GOPATH=${HOME}/.go
export PATH=$PATH:$GOPATH/bin
echo "# getting go dependencies (can take some time)..."
cd ${TEST_DIR}/../.. && go get
cd ${GOPATH}/src/github.com/ethereum/go-ethereum
git checkout poc-9
cd ${TEST_DIR} && go test

File diff suppressed because it is too large Load diff

View file

@ -1,496 +0,0 @@
/*
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() }

View file

@ -1,85 +0,0 @@
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)))
}

View file

@ -1,144 +0,0 @@
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)
}

View file

@ -1,46 +0,0 @@
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
}

View file

@ -1,387 +0,0 @@
// 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...)
}
}
}

View file

@ -1,280 +0,0 @@
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)
}

View file

@ -1,152 +0,0 @@
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
}

View file

@ -1,62 +0,0 @@
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)
})
}

View file

@ -1,40 +0,0 @@
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
}

View file

@ -1,72 +0,0 @@
# 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

View file

@ -1,106 +0,0 @@
// 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
}

View file

@ -1,4 +0,0 @@
.PHONY: test
test:
go test

View file

@ -1,190 +0,0 @@
# 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

View file

@ -1,9 +0,0 @@
// 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()

View file

@ -1,175 +0,0 @@
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
}

View file

@ -1,815 +0,0 @@
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
}

View file

@ -1,819 +0,0 @@
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:] // \ + <character>
} else {
str = str[2:] // \<character>
switch chr {
case 'b':
value = '\b'
case 'f':
value = '\f'
case 'n':
value = '\n'
case 'r':
value = '\r'
case 't':
value = '\t'
case 'v':
value = '\v'
case 'x', 'u':
size := 0
switch chr {
case 'x':
size = 2
case 'u':
size = 4
}
if len(str) < size {
return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
}
for j := 0; j < size; j++ {
decimal, ok := hex2decimal(str[j])
if !ok {
return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
}
value = value<<4 | decimal
}
str = str[size:]
if chr == 'x' {
break
}
if value > utf8.MaxRune {
panic("value > utf8.MaxRune")
}
case '0':
if len(str) == 0 || '0' > str[0] || str[0] > '7' {
value = 0
break
}
fallthrough
case '1', '2', '3', '4', '5', '6', '7':
// TODO strict
value = rune(chr) - '0'
j := 0
for ; j < 2; j++ {
if len(str) < j+1 {
break
}
chr := str[j]
if '0' > chr || chr > '7' {
break
}
decimal := rune(str[j]) - '0'
value = (value << 3) | decimal
}
str = str[j:]
case '\\':
value = '\\'
case '\'', '"':
value = rune(chr)
case '\r':
if len(str) > 0 {
if str[0] == '\n' {
str = str[1:]
}
}
fallthrough
case '\n':
continue
default:
value = rune(chr)
}
}
buffer.WriteRune(value)
}
return buffer.String(), nil
}
func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {
offset := self.chrOffset
tkn := token.NUMBER
if decimalPoint {
offset--
self.scanMantissa(10)
goto exponent
}
if self.chr == '0' {
offset := self.chrOffset
self.read()
if self.chr == 'x' || self.chr == 'X' {
// Hexadecimal
self.read()
if isDigit(self.chr, 16) {
self.read()
} else {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
self.scanMantissa(16)
if self.chrOffset-offset <= 2 {
// Only "0x" or "0X"
self.error(0, "Illegal hexadecimal number")
}
goto hexadecimal
} else if self.chr == '.' {
// Float
goto float
} else {
// Octal, Float
if self.chr == 'e' || self.chr == 'E' {
goto exponent
}
self.scanMantissa(8)
if self.chr == '8' || self.chr == '9' {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
goto octal
}
}
self.scanMantissa(10)
float:
if self.chr == '.' {
self.read()
self.scanMantissa(10)
}
exponent:
if self.chr == 'e' || self.chr == 'E' {
self.read()
if self.chr == '-' || self.chr == '+' {
self.read()
}
if isDecimalDigit(self.chr) {
self.read()
self.scanMantissa(10)
} else {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
}
hexadecimal:
octal:
if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
return tkn, self.str[offset:self.chrOffset]
}

View file

@ -1,380 +0,0 @@
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,
)
})
}

View file

@ -1,930 +0,0 @@
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"
}
}
}
}
}
}
}
]
`)
})
}

View file

@ -1,270 +0,0 @@
/*
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
}

File diff suppressed because it is too large Load diff

View file

@ -1,358 +0,0 @@
package parser
import (
"bytes"
"fmt"
"strconv"
)
type _RegExp_parser struct {
str string
length int
chr rune // The current character
chrOffset int // The offset of current character
offset int // The offset after current character (may be greater than 1)
errors []error
invalid bool // The input is an invalid JavaScript RegExp
goRegexp *bytes.Buffer
}
// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
//
// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
// backreference (\1, \2, ...) will cause an error.
//
// re2 (Go) has a different definition for \s: [\t\n\f\r ].
// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
//
// If the pattern is invalid (not valid even in JavaScript), then this function
// returns the empty string and an error.
//
// If the pattern is valid, but incompatible (contains a lookahead or backreference),
// then this function returns the transformation (a non-empty string) AND an error.
func TransformRegExp(pattern string) (string, error) {
if pattern == "" {
return "", nil
}
// TODO If without \, if without (?=, (?!, then another shortcut
parser := _RegExp_parser{
str: pattern,
length: len(pattern),
goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)),
}
parser.read() // Pull in the first character
parser.scan()
var err error
if len(parser.errors) > 0 {
err = parser.errors[0]
}
if parser.invalid {
return "", err
}
// Might not be re2 compatible, but is still a valid JavaScript RegExp
return parser.goRegexp.String(), err
}
func (self *_RegExp_parser) scan() {
for self.chr != -1 {
switch self.chr {
case '\\':
self.read()
self.scanEscape(false)
case '(':
self.pass()
self.scanGroup()
case '[':
self.pass()
self.scanBracket()
case ')':
self.error(-1, "Unmatched ')'")
self.invalid = true
self.pass()
default:
self.pass()
}
}
}
// (...)
func (self *_RegExp_parser) scanGroup() {
str := self.str[self.chrOffset:]
if len(str) > 1 { // A possibility of (?= or (?!
if str[0] == '?' {
if str[1] == '=' || str[1] == '!' {
self.error(-1, "re2: Invalid (%s) <lookahead>", self.str[self.chrOffset:self.chrOffset+2])
}
}
}
for self.chr != -1 && self.chr != ')' {
switch self.chr {
case '\\':
self.read()
self.scanEscape(false)
case '(':
self.pass()
self.scanGroup()
case '[':
self.pass()
self.scanBracket()
default:
self.pass()
continue
}
}
if self.chr != ')' {
self.error(-1, "Unterminated group")
self.invalid = true
return
}
self.pass()
}
// [...]
func (self *_RegExp_parser) scanBracket() {
for self.chr != -1 {
if self.chr == ']' {
break
} else if self.chr == '\\' {
self.read()
self.scanEscape(true)
continue
}
self.pass()
}
if self.chr != ']' {
self.error(-1, "Unterminated character class")
self.invalid = true
return
}
self.pass()
}
// \...
func (self *_RegExp_parser) scanEscape(inClass bool) {
offset := self.chrOffset
var length, base uint32
switch self.chr {
case '0', '1', '2', '3', '4', '5', '6', '7':
var value int64
size := 0
for {
digit := int64(digitValue(self.chr))
if digit >= 8 {
// Not a valid digit
break
}
value = value*8 + digit
self.read()
size += 1
}
if size == 1 { // The number of characters read
_, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'})
if err != nil {
self.errors = append(self.errors, err)
}
if value != 0 {
// An invalid backreference
self.error(-1, "re2: Invalid \\%d <backreference>", value)
}
return
}
tmp := []byte{'\\', 'x', '0', 0}
if value >= 16 {
tmp = tmp[0:2]
} else {
tmp = tmp[0:3]
}
tmp = strconv.AppendInt(tmp, value, 16)
_, err := self.goRegexp.Write(tmp)
if err != nil {
self.errors = append(self.errors, err)
}
return
case '8', '9':
size := 0
for {
digit := digitValue(self.chr)
if digit >= 10 {
// Not a valid digit
break
}
self.read()
size += 1
}
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
_, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset])
if err != nil {
self.errors = append(self.errors, err)
}
self.error(-1, "re2: Invalid \\%s <backreference>", self.str[offset:self.chrOffset])
return
case 'x':
self.read()
length, base = 2, 16
case 'u':
self.read()
length, base = 4, 16
case 'b':
if inClass {
_, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'})
if err != nil {
self.errors = append(self.errors, err)
}
self.read()
return
}
fallthrough
case 'B':
fallthrough
case 'd', 'D', 's', 'S', 'w', 'W':
// This is slightly broken, because ECMAScript
// includes \v in \s, \S, while re2 does not
fallthrough
case '\\':
fallthrough
case 'f', 'n', 'r', 't', 'v':
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
self.pass()
return
case 'c':
self.read()
var value int64
if 'a' <= self.chr && self.chr <= 'z' {
value = int64(self.chr) - 'a' + 1
} else if 'A' <= self.chr && self.chr <= 'Z' {
value = int64(self.chr) - 'A' + 1
} else {
err := self.goRegexp.WriteByte('c')
if err != nil {
self.errors = append(self.errors, err)
}
return
}
tmp := []byte{'\\', 'x', '0', 0}
if value >= 16 {
tmp = tmp[0:2]
} else {
tmp = tmp[0:3]
}
tmp = strconv.AppendInt(tmp, value, 16)
_, err := self.goRegexp.Write(tmp)
if err != nil {
self.errors = append(self.errors, err)
}
self.read()
return
default:
// $ is an identifier character, so we have to have
// a special case for it here
if self.chr == '$' || !isIdentifierPart(self.chr) {
// A non-identifier character needs escaping
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
} else {
// Unescape the character for re2
}
self.pass()
return
}
// Otherwise, we're a \u.... or \x...
valueOffset := self.chrOffset
var value uint32
{
length := length
for ; length > 0; length-- {
digit := uint32(digitValue(self.chr))
if digit >= base {
// Not a valid digit
goto skip
}
value = value*base + digit
self.read()
}
}
if length == 4 {
_, err := self.goRegexp.Write([]byte{
'\\',
'x',
'{',
self.str[valueOffset+0],
self.str[valueOffset+1],
self.str[valueOffset+2],
self.str[valueOffset+3],
'}',
})
if err != nil {
self.errors = append(self.errors, err)
}
} else if length == 2 {
_, err := self.goRegexp.Write([]byte{
'\\',
'x',
self.str[valueOffset+0],
self.str[valueOffset+1],
})
if err != nil {
self.errors = append(self.errors, err)
}
} else {
// Should never, ever get here...
self.error(-1, "re2: Illegal branch in scanEscape")
goto skip
}
return
skip:
_, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset])
if err != nil {
self.errors = append(self.errors, err)
}
}
func (self *_RegExp_parser) pass() {
if self.chr != -1 {
_, err := self.goRegexp.WriteRune(self.chr)
if err != nil {
self.errors = append(self.errors, err)
}
}
self.read()
}
// TODO Better error reporting, use the offset, etc.
func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error {
err := fmt.Errorf(msg, msgValues...)
self.errors = append(self.errors, err)
return err
}

View file

@ -1,149 +0,0 @@
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 (?=) <lookahead>")
test("(?=)", "re2: Invalid (?=) <lookahead>")
test("(?!)", "re2: Invalid (?!) <lookahead>")
// An error anyway
test("(?=", "re2: Invalid (?=) <lookahead>")
test("\\1", "re2: Invalid \\1 <backreference>")
test("\\90", "re2: Invalid \\90 <backreference>")
test("\\9123456789", "re2: Invalid \\9123456789 <backreference>")
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 (?!) <lookahead>")
test(")", "", "Unmatched ')'")
test("(?!))", "", "re2: Invalid (?!) <lookahead>")
test("\\0", "\\0", nil)
test("\\1", "\\1", "re2: Invalid \\1 <backreference>")
test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 <backreference>")
}
{
// 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)
})
}

View file

@ -1,44 +0,0 @@
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
}

View file

@ -1,662 +0,0 @@
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 "<into> 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 "<initializer> ;"
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()
}
}

View file

@ -1,51 +0,0 @@
# 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

View file

@ -1,47 +0,0 @@
/*
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
}

View file

@ -1,2 +0,0 @@
token_const.go: tokenfmt
./$^ | gofmt > $@

View file

@ -1,171 +0,0 @@
# 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

View file

@ -1,116 +0,0 @@
// 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
}

View file

@ -1,349 +0,0 @@
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,
},
}

View file

@ -1,222 +0,0 @@
#!/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_
}

View file

@ -1,9 +0,0 @@
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
}

View file

@ -1,276 +0,0 @@
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)
}

View file

@ -1,157 +0,0 @@
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)
}

View file

@ -6,8 +6,8 @@ TESTS := \
~
TEST := -v --run
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
TEST := -v
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
TEST := .
test: parser inline.go

View file

@ -60,7 +60,7 @@ 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()
return otto.Value{}
})
Set a Go function that returns something useful
@ -139,7 +139,6 @@ For more information: http://github.com/robertkrimen/otto/tree/master/underscore
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.
@ -205,16 +204,18 @@ the interrupt channel to do this:
}
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
}()
vm := otto.New()
vm.Interrupt = make(chan func())
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
go func() {
time.Sleep(2 * time.Second) // Stop after two seconds
vm.Interrupt <- func() {
panic(halt)
}
}()
vm.Run(unsafe) // Here be dragons (risky code)
vm.Interrupt = nil
}
Where is setTimeout/setInterval?
@ -242,6 +243,36 @@ Here is some more discussion of the issue:
var ErrVersion = errors.New("version mismatch")
```
#### type Error
```go
type Error struct {
}
```
An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
#### func (Error) Error
```go
func (err Error) Error() string
```
Error returns a description of the error
TypeError: 'def' is not a function
#### func (Error) String
```go
func (err Error) String() string
```
String returns a description of the error and a trace of where the error
occurred.
TypeError: 'def' is not a function
at xyz (<anonymous>:3:9)
at <anonymous>:7:1/
#### type FunctionCall
```go
@ -416,16 +447,16 @@ error if there was a problem during compilation.
#### func (*Otto) Copy
```go
func (self *Otto) Copy() *Otto
func (in *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.
Copy is useful for saving some 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.
This method works by walking the original runtime and cloning each object,
scope, stash, etc. into a new runtime.
Be on the lookout for memory leaks or inadvertent sharing of resources.
#### func (Otto) Get
@ -562,12 +593,10 @@ NullValue will return a Value representing null.
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.
otto/JavaScript
ToValue may be deprecated and removed in the near future.
Try Otto.ToValue for a replacement.
This function will not work for advanced types (struct, map, slice/array, etc.)
and you should use Otto.ToValue instead.
#### func TrueValue
@ -630,15 +659,13 @@ 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 returns an error, but it will always be nil. It is present for backwards
compatibility.
Export() interface{}
If a reasonable conversion is not possible, then the original value is returned.
If a reasonable conversion is not possible, then the original result is
returned.
undefined -> otto.Value (UndefinedValue())
null -> interface{}(nil)
undefined -> nil (FIXME?: Should be Value{})
null -> nil
boolean -> bool
number -> A number type (int, float32, uint64, ...)
string -> string

View file

@ -55,7 +55,7 @@ func Test_issue13(t *testing.T) {
t.Error(err)
t.FailNow()
}
is(result.toString(), "Xyzzy,42,def,ghi")
is(result.string(), "Xyzzy,42,def,ghi")
anything := struct {
Abc interface{}
@ -231,12 +231,12 @@ func Test_issue24(t *testing.T) {
}
{
vm.Set("abc", testStruct{Abc: true, Ghi: "Nothing happens."})
vm.Set("abc", _abcStruct{Abc: true, Ghi: "Nothing happens."})
value, err := vm.Get("abc")
is(err, nil)
export, _ := value.Export()
{
value, valid := export.(testStruct)
value, valid := export.(_abcStruct)
is(valid, true)
is(value.Abc, true)
@ -245,12 +245,12 @@ func Test_issue24(t *testing.T) {
}
{
vm.Set("abc", &testStruct{Abc: true, Ghi: "Nothing happens."})
vm.Set("abc", &_abcStruct{Abc: true, Ghi: "Nothing happens."})
value, err := vm.Get("abc")
is(err, nil)
export, _ := value.Export()
{
value, valid := export.(*testStruct)
value, valid := export.(*_abcStruct)
is(valid, true)
is(value.Abc, true)
@ -313,10 +313,22 @@ func Test_issue64(t *testing.T) {
})
}
func Test_issue73(t *testing.T) {
tt(t, func() {
test, vm := test()
vm.Set("abc", [4]int{3, 2, 1, 0})
test(`
var def = [ 0, 1, 2, 3 ];
JSON.stringify(def) + JSON.stringify(abc);
`, "[0,1,2,3][3,2,1,0]")
})
}
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")
@ -502,3 +514,104 @@ def"
`, "1,0,\t abc def,\t abc ")
})
}
func Test_issue79(t *testing.T) {
tt(t, func() {
test, vm := test()
vm.Set("abc", []_abcStruct{
{
Ghi: "一",
Def: 1,
},
{
Def: 3,
Ghi: "三",
},
{
Def: 2,
Ghi: "二",
},
{
Def: 4,
Ghi: "四",
},
})
test(`
abc.sort(function(a,b){ return b.Def-a.Def });
def = [];
for (i = 0; i < abc.length; i++) {
def.push(abc[i].String())
}
def;
`, "四,三,二,一")
})
}
func Test_issue80(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`
JSON.stringify([
1401868959,
14018689591,
140186895901,
1401868959001,
14018689590001,
140186895900001,
1401868959000001,
1401868959000001.5,
14018689590000001,
140186895900000001,
1401868959000000001,
14018689590000000001,
140186895900000000001,
140186895900000000001.5
]);
`, "[1401868959,14018689591,140186895901,1401868959001,14018689590001,140186895900001,1401868959000001,1.4018689590000015e+15,14018689590000001,140186895900000001,1401868959000000001,1.401868959e+19,1.401868959e+20,1.401868959e+20]")
})
}
func Test_issue87(t *testing.T) {
tt(t, func() {
test, vm := test()
test(`
var def = 0;
abc: {
for (;;) {
def = !1;
break abc;
}
def = !0;
}
def;
`, false)
_, err := vm.Run(`
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new r.init(c,a)}}),l=f.enc={},k=l.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,
2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}},
u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);c.sigBytes-=b}return new r.init(g,b)},clone:function(){var a=m.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});g.Hasher=u.extend({cfg:m.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){u.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new t.HMAC.init(a,
d)).finalize(c)}}});var t=f.algo={};return f}(Math);
(function(h){for(var s=CryptoJS,f=s.lib,g=f.WordArray,q=f.Hasher,f=s.algo,m=[],r=[],l=function(a){return 4294967296*(a-(a|0))|0},k=2,n=0;64>n;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]=
c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math);
(function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j<h;j++)k[j]^=1549556828,n[j]^=909522486;r.sigBytes=l.sigBytes=m;this.reset()},reset:function(){var f=this._hasher;f.reset();f.update(this._iKey)},update:function(f){this._hasher.update(f);return this},finalize:function(f){var g=
this._hasher;f=g.finalize(f);g.reset();return g.finalize(this._oKey.clone().concat(f))}})})();
`)
is(err, nil)
test(`CryptoJS.HmacSHA256("Message", "secret");`, "aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597")
})
}

View file

@ -2,7 +2,6 @@ package otto
import (
"encoding/hex"
"fmt"
"math"
"net/url"
"regexp"
@ -19,25 +18,26 @@ func builtinGlobal_eval(call FunctionCall) Value {
return src
}
runtime := call.runtime
program := runtime.cmpl_parseOrThrow(toString(src))
if call.evalHint {
runtime.EnterEvalExecutionContext(call)
defer runtime.LeaveExecutionContext()
program := runtime.cmpl_parseOrThrow(src.string())
if !call.eval {
// Not a direct call to eval, so we enter the global ExecutionContext
runtime.enterGlobalScope()
defer runtime.leaveScope()
}
returnValue := runtime.cmpl_evaluate_nodeProgram(program)
returnValue := runtime.cmpl_evaluate_nodeProgram(program, true)
if returnValue.isEmpty() {
return UndefinedValue()
return Value{}
}
return returnValue
}
func builtinGlobal_isNaN(call FunctionCall) Value {
value := toFloat(call.Argument(0))
value := call.Argument(0).float64()
return toValue_bool(math.IsNaN(value))
}
func builtinGlobal_isFinite(call FunctionCall) Value {
value := toFloat(call.Argument(0))
value := call.Argument(0).float64()
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
}
@ -70,7 +70,7 @@ func digitValue(chr rune) int {
}
func builtinGlobal_parseInt(call FunctionCall) Value {
input := strings.TrimSpace(toString(call.Argument(0)))
input := strings.TrimSpace(call.Argument(0).string())
if len(input) == 0 {
return NaNValue()
}
@ -153,7 +153,7 @@ 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)))
input := strings.TrimSpace(call.Argument(0).string())
if parseFloat_matchBadSpecial.MatchString(input) {
return NaNValue()
}
@ -185,7 +185,7 @@ func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
case []uint16:
input = vl
default:
input = utf16.Encode([]rune(toString(value)))
input = utf16.Encode([]rune(value.string()))
}
if len(input) == 0 {
return toValue_string("")
@ -197,18 +197,17 @@ func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
value := input[index]
decode := utf16.Decode(input[index : index+1])
if value >= 0xDC00 && value <= 0xDFFF {
panic(newURIError("URI malformed"))
panic(call.runtime.panicURIError("URI malformed"))
}
if value >= 0xD800 && value <= 0xDBFF {
index += 1
if index >= length {
panic(newURIError("URI malformed"))
panic(call.runtime.panicURIError("URI malformed"))
}
// input = ..., value, value1, ...
value = value
value1 := input[index]
if value1 < 0xDC00 || value1 > 0xDFFF {
panic(newURIError("URI malformed"))
panic(call.runtime.panicURIError("URI malformed"))
}
decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
}
@ -257,17 +256,17 @@ func _decodeURI(input string, reserve bool) (string, bool) {
}
func builtinGlobal_decodeURI(call FunctionCall) Value {
output, err := _decodeURI(toString(call.Argument(0)), true)
output, err := _decodeURI(call.Argument(0).string(), true)
if err {
panic(newURIError("URI malformed"))
panic(call.runtime.panicURIError("URI malformed"))
}
return toValue_string(output)
}
func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
output, err := _decodeURI(toString(call.Argument(0)), false)
output, err := _decodeURI(call.Argument(0).string(), false)
if err {
panic(newURIError("URI malformed"))
panic(call.runtime.panicURIError("URI malformed"))
}
return toValue_string(output)
}
@ -346,48 +345,9 @@ func builtin_unescape(input string) string {
}
func builtinGlobal_escape(call FunctionCall) Value {
return toValue_string(builtin_escape(toString(call.Argument(0))))
return toValue_string(builtin_escape(call.Argument(0).string()))
}
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))
return toValue_string(builtin_unescape(call.Argument(0).string()))
}

View file

@ -11,7 +11,7 @@ func builtinArray(call FunctionCall) Value {
return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
}
func builtinNewArray(self *_object, _ Value, argumentList []Value) Value {
func builtinNewArray(self *_object, argumentList []Value) Value {
return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
}
@ -19,7 +19,7 @@ func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
if len(argumentList) == 1 {
firstArgument := argumentList[0]
if firstArgument.IsNumber() {
return runtime.newArray(arrayUint32(firstArgument))
return runtime.newArray(arrayUint32(runtime, firstArgument))
}
}
return runtime.newArrayOf(argumentList)
@ -30,7 +30,7 @@ func builtinArray_toString(call FunctionCall) Value {
join := thisObject.get("join")
if join.isCallable() {
join := join._object()
return join.Call(call.This, call.ArgumentList)
return join.call(call.This, call.ArgumentList, false, nativeFrame)
}
return builtinObject_toString(call)
}
@ -46,15 +46,15 @@ func builtinArray_toLocaleString(call FunctionCall) Value {
for index := int64(0); index < length; index += 1 {
value := thisObject.get(arrayIndexToString(index))
stringValue := ""
switch value._valueType {
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
default:
object := call.runtime.toObject(value)
toLocaleString := object.get("toLocaleString")
if !toLocaleString.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
stringValue = toLocaleString.call(toValue_object(object)).toString()
stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
}
stringList = append(stringList, stringValue)
}
@ -66,11 +66,11 @@ func builtinArray_concat(call FunctionCall) Value {
valueArray := []Value{}
source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
for _, item := range source {
switch item._valueType {
switch item.kind {
case valueObject:
object := item._object()
if isArray(object) {
length := toInteger(object.get("length")).value
length := object.get("length").number().int64
for index := int64(0); index < length; index += 1 {
name := strconv.FormatInt(index, 10)
if object.hasProperty(name) {
@ -94,7 +94,7 @@ func builtinArray_shift(call FunctionCall) Value {
length := int64(toUint32(thisObject.get("length")))
if 0 == length {
thisObject.put("length", toValue_int64(0), true)
return UndefinedValue()
return Value{}
}
first := thisObject.get("0")
for index := int64(1); index < length; index++ {
@ -130,7 +130,7 @@ func builtinArray_pop(call FunctionCall) Value {
length := int64(toUint32(thisObject.get("length")))
if 0 == length {
thisObject.put("length", toValue_uint32(0), true)
return UndefinedValue()
return Value{}
}
last := thisObject.get(arrayIndexToString(length - 1))
thisObject.delete(arrayIndexToString(length-1), true)
@ -143,7 +143,7 @@ func builtinArray_join(call FunctionCall) Value {
{
argument := call.Argument(0)
if argument.IsDefined() {
separator = toString(argument)
separator = argument.string()
}
}
thisObject := call.thisObject()
@ -155,10 +155,10 @@ func builtinArray_join(call FunctionCall) Value {
for index := int64(0); index < length; index += 1 {
value := thisObject.get(arrayIndexToString(index))
stringValue := ""
switch value._valueType {
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
default:
stringValue = toString(value)
stringValue = value.string()
}
stringList = append(stringList, stringValue)
}
@ -370,8 +370,8 @@ func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int
}
if compare == nil {
j.value = toString(x)
k.value = toString(y)
j.value = x.string()
k.value = y.string()
if j.value == k.value {
return 0
@ -382,7 +382,7 @@ func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int
return 1
}
return int(toInt32(compare.Call(UndefinedValue(), []Value{x, y})))
return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame)))
}
func arraySortSwap(thisObject *_object, index0, index1 uint) {
@ -447,7 +447,7 @@ func builtinArray_sort(call FunctionCall) Value {
compare := compareValue._object()
if compareValue.IsUndefined() {
} else if !compareValue.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
if length > 1 {
arraySortQuickSort(thisObject, 0, length-1, compare)
@ -464,7 +464,7 @@ func builtinArray_indexOf(call FunctionCall) Value {
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
index := int64(0)
if len(call.ArgumentList) > 1 {
index = toInteger(call.Argument(1)).value
index = call.Argument(1).number().int64
}
if index < 0 {
if index += length; index < 0 {
@ -492,7 +492,7 @@ func builtinArray_lastIndexOf(call FunctionCall) Value {
length := int64(toUint32(thisObject.get("length")))
index := length - 1
if len(call.ArgumentList) > 1 {
index = toInteger(call.Argument(1)).value
index = call.Argument(1).number().int64
}
if 0 > index {
index += length
@ -523,15 +523,15 @@ func builtinArray_every(call FunctionCall) Value {
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() {
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
continue
}
return FalseValue()
return falseValue
}
}
return TrueValue()
return trueValue
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_some(call FunctionCall) Value {
@ -542,14 +542,14 @@ func builtinArray_some(call FunctionCall) Value {
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()
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
return trueValue
}
}
}
return FalseValue()
return falseValue
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_forEach(call FunctionCall) Value {
@ -560,12 +560,12 @@ func builtinArray_forEach(call FunctionCall) Value {
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)
iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
}
}
return UndefinedValue()
return Value{}
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_map(call FunctionCall) Value {
@ -577,14 +577,14 @@ func builtinArray_map(call FunctionCall) Value {
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)
values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
} else {
values[index] = UndefinedValue()
values[index] = Value{}
}
}
return toValue_object(call.runtime.newArrayOf(values))
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_filter(call FunctionCall) Value {
@ -597,14 +597,14 @@ func builtinArray_filter(call FunctionCall) Value {
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() {
if iterator.call(call.runtime, callThis, value, index, this).bool() {
values = append(values, value)
}
}
}
return toValue_object(call.runtime.newArrayOf(values))
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_reduce(call FunctionCall) Value {
@ -630,13 +630,13 @@ func builtinArray_reduce(call FunctionCall) Value {
}
for ; index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = iterator.call(UndefinedValue(), accumulator, thisObject.get(key), key, this)
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
}
}
return accumulator
}
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinArray_reduceRight(call FunctionCall) Value {
@ -662,11 +662,11 @@ func builtinArray_reduceRight(call FunctionCall) Value {
}
for ; index >= 0; index-- {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = iterator.call(UndefinedValue(), accumulator, thisObject.get(key), key, this)
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
}
}
return accumulator
}
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}

View file

@ -3,10 +3,10 @@ package otto
// Boolean
func builtinBoolean(call FunctionCall) Value {
return toValue_bool(toBoolean(call.Argument(0)))
return toValue_bool(call.Argument(0).bool())
}
func builtinNewBoolean(self *_object, _ Value, argumentList []Value) Value {
func builtinNewBoolean(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0)))
}
@ -16,7 +16,7 @@ func builtinBoolean_toString(call FunctionCall) Value {
// Will throw a TypeError if ThisObject is not a Boolean
value = call.thisClassObject("Boolean").primitiveValue()
}
return toValue_string(toString(value))
return toValue_string(value.string())
}
func builtinBoolean_valueOf(call FunctionCall) Value {

View file

@ -21,12 +21,12 @@ func builtinDate(call FunctionCall) Value {
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
}
func builtinNewDate(self *_object, _ Value, argumentList []Value) Value {
func builtinNewDate(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
}
func builtinDate_toString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -34,7 +34,7 @@ func builtinDate_toString(call FunctionCall) Value {
}
func builtinDate_toDateString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -42,7 +42,7 @@ func builtinDate_toDateString(call FunctionCall) Value {
}
func builtinDate_toTimeString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -50,7 +50,7 @@ func builtinDate_toTimeString(call FunctionCall) Value {
}
func builtinDate_toUTCString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -58,7 +58,7 @@ func builtinDate_toUTCString(call FunctionCall) Value {
}
func builtinDate_toISOString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -69,20 +69,21 @@ func builtinDate_toJSON(call FunctionCall) Value {
object := call.thisObject()
value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
{ // FIXME value.isFinite
value := toFloat(value)
value := value.float64()
if math.IsNaN(value) || math.IsInf(value, 0) {
return NullValue()
return nullValue
}
}
toISOString := object.get("toISOString")
if !toISOString.isCallable() {
panic(newTypeError())
// FIXME
panic(call.runtime.panicTypeError())
}
return toISOString.call(toValue_object(object), []Value{})
return toISOString.call(call.runtime, toValue_object(object), []Value{})
}
func builtinDate_toGMTString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -90,7 +91,7 @@ func builtinDate_toGMTString(call FunctionCall) Value {
}
func builtinDate_getTime(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -101,15 +102,15 @@ func builtinDate_getTime(call FunctionCall) Value {
func builtinDate_setTime(call FunctionCall) Value {
object := call.thisObject()
date := dateObjectOf(object)
date.Set(toFloat(call.Argument(0)))
date := dateObjectOf(call.runtime, call.thisObject())
date.Set(call.Argument(0).float64())
object.value = date
return date.Value()
}
func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
object := call.thisObject()
date := dateObjectOf(object)
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return nil, nil, nil, nil
}
@ -126,16 +127,14 @@ func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool
valueList := make([]int, argumentLimit)
for index := 0; index < argumentLimit; index++ {
value := call.ArgumentList[index]
if value.IsNaN() {
nm := value.number()
switch nm.kind {
case numberInteger, numberFloat:
default:
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)
valueList[index] = int(nm.int64)
}
baseTime := date.Time()
if timeLocal {
@ -146,7 +145,7 @@ func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool
}
func builtinDate_parse(call FunctionCall) Value {
date := toString(call.Argument(0))
date := call.Argument(0).string()
return toValue_float64(dateParse(date))
}
@ -161,7 +160,7 @@ func builtinDate_now(call FunctionCall) Value {
// This is a placeholder
func builtinDate_toLocaleString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -170,7 +169,7 @@ func builtinDate_toLocaleString(call FunctionCall) Value {
// This is a placeholder
func builtinDate_toLocaleDateString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -179,7 +178,7 @@ func builtinDate_toLocaleDateString(call FunctionCall) Value {
// This is a placeholder
func builtinDate_toLocaleTimeString(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
@ -187,7 +186,7 @@ func builtinDate_toLocaleTimeString(call FunctionCall) Value {
}
func builtinDate_valueOf(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -197,7 +196,7 @@ func builtinDate_valueOf(call FunctionCall) 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())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -207,7 +206,7 @@ func builtinDate_getYear(call FunctionCall) Value {
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())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -215,7 +214,7 @@ func builtinDate_getFullYear(call FunctionCall) Value {
}
func builtinDate_getUTCFullYear(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -223,7 +222,7 @@ func builtinDate_getUTCFullYear(call FunctionCall) Value {
}
func builtinDate_getMonth(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -231,7 +230,7 @@ func builtinDate_getMonth(call FunctionCall) Value {
}
func builtinDate_getUTCMonth(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -239,7 +238,7 @@ func builtinDate_getUTCMonth(call FunctionCall) Value {
}
func builtinDate_getDate(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -247,7 +246,7 @@ func builtinDate_getDate(call FunctionCall) Value {
}
func builtinDate_getUTCDate(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -256,7 +255,7 @@ func builtinDate_getUTCDate(call FunctionCall) Value {
func builtinDate_getDay(call FunctionCall) Value {
// Actually day of the week
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -264,7 +263,7 @@ func builtinDate_getDay(call FunctionCall) Value {
}
func builtinDate_getUTCDay(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -272,7 +271,7 @@ func builtinDate_getUTCDay(call FunctionCall) Value {
}
func builtinDate_getHours(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -280,7 +279,7 @@ func builtinDate_getHours(call FunctionCall) Value {
}
func builtinDate_getUTCHours(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -288,7 +287,7 @@ func builtinDate_getUTCHours(call FunctionCall) Value {
}
func builtinDate_getMinutes(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -296,7 +295,7 @@ func builtinDate_getMinutes(call FunctionCall) Value {
}
func builtinDate_getUTCMinutes(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -304,7 +303,7 @@ func builtinDate_getUTCMinutes(call FunctionCall) Value {
}
func builtinDate_getSeconds(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -312,7 +311,7 @@ func builtinDate_getSeconds(call FunctionCall) Value {
}
func builtinDate_getUTCSeconds(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -320,7 +319,7 @@ func builtinDate_getUTCSeconds(call FunctionCall) Value {
}
func builtinDate_getMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -328,7 +327,7 @@ func builtinDate_getMilliseconds(call FunctionCall) Value {
}
func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
@ -336,7 +335,7 @@ func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
}
func builtinDate_getTimezoneOffset(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}

View file

@ -0,0 +1,126 @@
package otto
import (
"fmt"
)
func builtinError(call FunctionCall) Value {
return toValue_object(call.runtime.newError("", call.Argument(0)))
}
func builtinNewError(self *_object, 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(call.runtime.panicTypeError())
}
name := "Error"
nameValue := thisObject.get("name")
if nameValue.IsDefined() {
name = nameValue.string()
}
message := ""
messageValue := thisObject.get("message")
if messageValue.IsDefined() {
message = messageValue.string()
}
if len(name) == 0 {
return toValue_string(message)
}
if len(message) == 0 {
return toValue_string(name)
}
return toValue_string(fmt.Sprintf("%s: %s", name, message))
}
func (runtime *_runtime) newEvalError(message Value) *_object {
self := runtime.newErrorObject("EvalError", message)
self.prototype = runtime.global.EvalErrorPrototype
return self
}
func builtinEvalError(call FunctionCall) Value {
return toValue_object(call.runtime.newEvalError(call.Argument(0)))
}
func builtinNewEvalError(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0)))
}
func (runtime *_runtime) newTypeError(message Value) *_object {
self := runtime.newErrorObject("TypeError", message)
self.prototype = runtime.global.TypeErrorPrototype
return self
}
func builtinTypeError(call FunctionCall) Value {
return toValue_object(call.runtime.newTypeError(call.Argument(0)))
}
func builtinNewTypeError(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0)))
}
func (runtime *_runtime) newRangeError(message Value) *_object {
self := runtime.newErrorObject("RangeError", message)
self.prototype = runtime.global.RangeErrorPrototype
return self
}
func builtinRangeError(call FunctionCall) Value {
return toValue_object(call.runtime.newRangeError(call.Argument(0)))
}
func builtinNewRangeError(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0)))
}
func (runtime *_runtime) newURIError(message Value) *_object {
self := runtime.newErrorObject("URIError", message)
self.prototype = runtime.global.URIErrorPrototype
return self
}
func (runtime *_runtime) newReferenceError(message Value) *_object {
self := runtime.newErrorObject("ReferenceError", 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, argumentList []Value) Value {
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0)))
}
func (runtime *_runtime) newSyntaxError(message Value) *_object {
self := runtime.newErrorObject("SyntaxError", 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, argumentList []Value) Value {
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0)))
}
func builtinURIError(call FunctionCall) Value {
return toValue_object(call.runtime.newURIError(call.Argument(0)))
}
func builtinNewURIError(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0)))
}

View file

@ -1,6 +1,7 @@
package otto
import (
"fmt"
"regexp"
"strings"
"unicode"
@ -14,14 +15,14 @@ func builtinFunction(call FunctionCall) Value {
return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList))
}
func builtinNewFunction(self *_object, _ Value, argumentList []Value) Value {
func builtinNewFunction(self *_object, argumentList []Value) Value {
return toValue_object(builtinNewFunctionNative(self.runtime, argumentList))
}
func argumentList2parameterList(argumentList []Value) []string {
parameterList := make([]string, 0, len(argumentList))
for _, value := range argumentList {
tmp := strings.FieldsFunc(toString(value), func(chr rune) bool {
tmp := strings.FieldsFunc(value.string(), func(chr rune) bool {
return chr == ',' || unicode.IsSpace(chr)
})
parameterList = append(parameterList, tmp...)
@ -37,40 +38,51 @@ func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object
if count > 0 {
tmp := make([]string, 0, count-1)
for _, value := range argumentList[0 : count-1] {
tmp = append(tmp, toString(value))
tmp = append(tmp, value.string())
}
parameterList = strings.Join(tmp, ",")
body = toString(argumentList[count-1])
body = argumentList[count-1].string()
}
// FIXME
function, err := parser.ParseFunction(parameterList, body)
runtime.parseThrow(err) // Will panic/throw appropriately
cmpl_function := parseExpression(function)
cmpl := _compiler{}
cmpl_function := cmpl.parseExpression(function)
return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.GlobalEnvironment)
return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash)
}
func builtinFunction_toString(call FunctionCall) Value {
object := call.thisClassObject("Function") // Should throw a TypeError unless Function
return toValue_string(object.value.(_functionObject).source(object))
switch fn := object.value.(type) {
case _nativeFunctionObject:
return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name))
case _nodeFunctionObject:
return toValue_string(fn.node.source)
case _bindFunctionObject:
return toValue_string("function () { [native code] }")
}
panic(call.runtime.panicTypeError("Function.toString()"))
}
func builtinFunction_apply(call FunctionCall) Value {
if !call.This.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
this := call.Argument(0)
if this.IsUndefined() {
// FIXME Not ECMA5
this = toValue_object(call.runtime.GlobalObject)
this = toValue_object(call.runtime.globalObject)
}
argumentList := call.Argument(1)
switch argumentList._valueType {
switch argumentList.kind {
case valueUndefined, valueNull:
return call.thisObject().Call(this, []Value{})
return call.thisObject().call(this, nil, false, nativeFrame)
case valueObject:
default:
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
arrayObject := argumentList._object()
@ -80,29 +92,29 @@ func builtinFunction_apply(call FunctionCall) Value {
for index := int64(0); index < length; index++ {
valueArray[index] = arrayObject.get(arrayIndexToString(index))
}
return thisObject.Call(this, valueArray)
return thisObject.call(this, valueArray, false, nativeFrame)
}
func builtinFunction_call(call FunctionCall) Value {
if !call.This.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
thisObject := call.thisObject()
this := call.Argument(0)
if this.IsUndefined() {
// FIXME Not ECMA5
this = toValue_object(call.runtime.GlobalObject)
this = toValue_object(call.runtime.globalObject)
}
if len(call.ArgumentList) >= 1 {
return thisObject.Call(this, call.ArgumentList[1:])
return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame)
}
return thisObject.Call(this, []Value{})
return thisObject.call(this, nil, false, nativeFrame)
}
func builtinFunction_bind(call FunctionCall) Value {
target := call.This
if !target.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
targetObject := target._object()
@ -110,7 +122,7 @@ func builtinFunction_bind(call FunctionCall) Value {
argumentList := call.slice(1)
if this.IsUndefined() {
// FIXME Do this elsewhere?
this = toValue_object(call.runtime.GlobalObject)
this = toValue_object(call.runtime.globalObject)
}
return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList))

View file

@ -3,7 +3,7 @@ package otto
import (
"bytes"
"encoding/json"
"math"
"fmt"
"strings"
)
@ -23,13 +23,13 @@ func builtinJSON_parse(call FunctionCall) Value {
}
var root interface{}
err := json.Unmarshal([]byte(toString(call.Argument(0))), &root)
err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
if err != nil {
panic(newSyntaxError(err.Error()))
panic(call.runtime.panicSyntaxError(err.Error()))
}
value, exists := builtinJSON_parseWalk(ctx, root)
if !exists {
value = UndefinedValue()
value = Value{}
}
if revive {
root := ctx.call.runtime.newObject()
@ -65,13 +65,13 @@ func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name
})
}
}
return ctx.reviver.call(toValue_object(holder), name, value)
return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
}
func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
switch value := rawValue.(type) {
case nil:
return NullValue(), true
return nullValue, true
case bool:
return toValue_bool(value), true
case string:
@ -120,7 +120,7 @@ func builtinJSON_stringify(call FunctionCall) Value {
length = 0
for index, _ := range propertyList {
value := replacer.get(arrayIndexToString(int64(index)))
switch value._valueType {
switch value.kind {
case valueObject:
switch value.value.(*_object).class {
case "String":
@ -133,7 +133,7 @@ func builtinJSON_stringify(call FunctionCall) Value {
default:
continue
}
name := toString(value)
name := value.string()
if seen[name] {
continue
}
@ -148,24 +148,24 @@ func builtinJSON_stringify(call FunctionCall) Value {
}
}
if spaceValue, exists := call.getArgument(2); exists {
if spaceValue._valueType == valueObject {
if spaceValue.kind == valueObject {
switch spaceValue.value.(*_object).class {
case "String":
spaceValue = toValue_string(toString(spaceValue))
spaceValue = toValue_string(spaceValue.string())
case "Number":
spaceValue = toNumber(spaceValue)
spaceValue = spaceValue.numberValue()
}
}
switch spaceValue._valueType {
switch spaceValue.kind {
case valueString:
value := toString(spaceValue)
value := spaceValue.string()
if len(value) > 10 {
ctx.gap = value[0:10]
} else {
ctx.gap = value
}
case valueNumber:
value := toInteger(spaceValue).value
value := spaceValue.number().int64
if value > 10 {
value = 10
} else if value < 0 {
@ -178,11 +178,11 @@ func builtinJSON_stringify(call FunctionCall) Value {
holder.put("", call.Argument(0), false)
value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
if !exists {
return UndefinedValue()
return Value{}
}
valueJSON, err := json.Marshal(value)
if err != nil {
panic(newTypeError(err.Error()))
panic(call.runtime.panicTypeError(err.Error()))
}
if ctx.gap != "" {
valueJSON1 := bytes.Buffer{}
@ -198,7 +198,7 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
if value.IsObject() {
object := value._object()
if toJSON := object.get("toJSON"); toJSON.IsFunction() {
value = toJSON.call(value, key)
value = toJSON.call(ctx.call.runtime, value, key)
} else {
// If the object is a GoStruct or something that implements json.Marshaler
if object.objectClass.marshalJSON != nil {
@ -211,31 +211,35 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
}
if ctx.replacerFunction != nil {
value = (*ctx.replacerFunction).call(toValue_object(holder), key, value)
value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
}
if value._valueType == valueObject {
if value.kind == valueObject {
switch value.value.(*_object).class {
case "Boolean":
value = value._object().value.(Value)
case "String":
value = toValue_string(toString(value))
value = toValue_string(value.string())
case "Number":
value = toNumber(value)
value = value.numberValue()
}
}
switch value._valueType {
switch value.kind {
case valueBoolean:
return toBoolean(value), true
return value.bool(), true
case valueString:
return toString(value), true
return value.string(), true
case valueNumber:
value := toFloat(value)
if math.IsNaN(value) || math.IsInf(value, 0) {
integer := value.number()
switch integer.kind {
case numberInteger:
return integer.int64, true
case numberFloat:
return integer.float64, true
default:
return nil, true
}
return value, true
case valueNull:
return nil, true
case valueObject:
@ -243,14 +247,24 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
if value := value._object(); nil != value {
for _, object := range ctx.stack {
if holder == object {
panic(newTypeError("Converting circular structure to JSON"))
panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
}
}
ctx.stack = append(ctx.stack, value)
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
}
if isArray(holder) {
length := holder.get("length").value.(uint32)
var length uint32
switch value := holder.get("length").value.(type) {
case uint32:
length = value
case int:
if value >= 0 {
length = uint32(value)
}
default:
panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
}
array := make([]interface{}, length)
for index, _ := range array {
name := arrayIndexToString(int64(index))

View file

@ -8,31 +8,31 @@ import (
// Math
func builtinMath_abs(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Abs(number))
}
func builtinMath_acos(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Acos(number))
}
func builtinMath_asin(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Asin(number))
}
func builtinMath_atan(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Atan(number))
}
func builtinMath_atan2(call FunctionCall) Value {
y := toFloat(call.Argument(0))
y := call.Argument(0).float64()
if math.IsNaN(y) {
return NaNValue()
}
x := toFloat(call.Argument(1))
x := call.Argument(1).float64()
if math.IsNaN(x) {
return NaNValue()
}
@ -40,27 +40,27 @@ func builtinMath_atan2(call FunctionCall) Value {
}
func builtinMath_cos(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Cos(number))
}
func builtinMath_ceil(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Ceil(number))
}
func builtinMath_exp(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Exp(number))
}
func builtinMath_floor(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Floor(number))
}
func builtinMath_log(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Log(number))
}
@ -69,14 +69,14 @@ func builtinMath_max(call FunctionCall) Value {
case 0:
return negativeInfinityValue()
case 1:
return toValue_float64(toFloat(call.ArgumentList[0]))
return toValue_float64(call.ArgumentList[0].float64())
}
result := toFloat(call.ArgumentList[0])
result := call.ArgumentList[0].float64()
if math.IsNaN(result) {
return NaNValue()
}
for _, value := range call.ArgumentList[1:] {
value := toFloat(value)
value := value.float64()
if math.IsNaN(value) {
return NaNValue()
}
@ -90,14 +90,14 @@ func builtinMath_min(call FunctionCall) Value {
case 0:
return positiveInfinityValue()
case 1:
return toValue_float64(toFloat(call.ArgumentList[0]))
return toValue_float64(call.ArgumentList[0].float64())
}
result := toFloat(call.ArgumentList[0])
result := call.ArgumentList[0].float64()
if math.IsNaN(result) {
return NaNValue()
}
for _, value := range call.ArgumentList[1:] {
value := toFloat(value)
value := value.float64()
if math.IsNaN(value) {
return NaNValue()
}
@ -108,8 +108,8 @@ func builtinMath_min(call FunctionCall) Value {
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))
x := call.Argument(0).float64()
y := call.Argument(1).float64()
if math.Abs(x) == 1 && math.IsInf(y, 0) {
return NaNValue()
}
@ -121,7 +121,7 @@ func builtinMath_random(call FunctionCall) Value {
}
func builtinMath_round(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
value := math.Floor(number + 0.5)
if value == 0 {
value = math.Copysign(0, number)
@ -130,16 +130,16 @@ func builtinMath_round(call FunctionCall) Value {
}
func builtinMath_sin(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Sin(number))
}
func builtinMath_sqrt(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Sqrt(number))
}
func builtinMath_tan(call FunctionCall) Value {
number := toFloat(call.Argument(0))
number := call.Argument(0).float64()
return toValue_float64(math.Tan(number))
}

View file

@ -9,7 +9,7 @@ import (
func numberValueFromNumberArgumentList(argumentList []Value) Value {
if len(argumentList) > 0 {
return toNumber(argumentList[0])
return argumentList[0].numberValue()
}
return toValue_int(0)
}
@ -18,7 +18,7 @@ func builtinNumber(call FunctionCall) Value {
return numberValueFromNumberArgumentList(call.ArgumentList)
}
func builtinNewNumber(self *_object, _ Value, argumentList []Value) Value {
func builtinNewNumber(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList)))
}
@ -30,12 +30,12 @@ func builtinNumber_toString(call FunctionCall) Value {
if radixArgument.IsDefined() {
integer := toIntegerFloat(radixArgument)
if integer < 2 || integer > 36 {
panic(newRangeError("RangeError: toString() radix must be between 2 and 36"))
panic(call.runtime.panicRangeError("RangeError: toString() radix must be between 2 and 36"))
}
radix = int(integer)
}
if radix == 10 {
return toValue_string(toString(value))
return toValue_string(value.string())
}
return toValue_string(numberToStringRadix(value, radix))
}
@ -47,16 +47,16 @@ func builtinNumber_valueOf(call FunctionCall) Value {
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"))
panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20"))
}
if call.This.IsNaN() {
return toValue_string("NaN")
}
value := toFloat(call.This)
value := call.This.float64()
if math.Abs(value) >= 1e21 {
return toValue_string(floatToString(value, 64))
}
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'f', int(precision), 64))
return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64))
}
func builtinNumber_toExponential(call FunctionCall) Value {
@ -67,10 +67,10 @@ func builtinNumber_toExponential(call FunctionCall) Value {
if value := call.Argument(0); value.IsDefined() {
precision = toIntegerFloat(value)
if 0 > precision {
panic(newRangeError("RangeError: toExponential() precision must be greater than 0"))
panic(call.runtime.panicRangeError("RangeError: toString() radix must be between 2 and 36"))
}
}
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'e', int(precision), 64))
return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64))
}
func builtinNumber_toPrecision(call FunctionCall) Value {
@ -79,13 +79,13 @@ func builtinNumber_toPrecision(call FunctionCall) Value {
}
value := call.Argument(0)
if value.IsUndefined() {
return toValue_string(toString(call.This))
return toValue_string(call.This.string())
}
precision := toIntegerFloat(value)
if 1 > precision {
panic(newRangeError("RangeError: toPrecision() precision must be greater than 1"))
panic(call.runtime.panicRangeError("RangeError: toPrecision() precision must be greater than 1"))
}
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'g', int(precision), 64))
return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64))
}
func builtinNumber_toLocaleString(call FunctionCall) Value {

View file

@ -8,7 +8,7 @@ import (
func builtinObject(call FunctionCall) Value {
value := call.Argument(0)
switch value._valueType {
switch value.kind {
case valueUndefined, valueNull:
return toValue_object(call.runtime.newObject())
}
@ -16,9 +16,9 @@ func builtinObject(call FunctionCall) Value {
return toValue_object(call.runtime.toObject(value))
}
func builtinNewObject(self *_object, _ Value, argumentList []Value) Value {
func builtinNewObject(self *_object, argumentList []Value) Value {
value := valueOfArrayIndex(argumentList, 0)
switch value._valueType {
switch value.kind {
case valueNull, valueUndefined:
case valueNumber, valueString, valueBoolean:
return toValue_object(self.runtime.toObject(value))
@ -34,7 +34,7 @@ func builtinObject_valueOf(call FunctionCall) Value {
}
func builtinObject_hasOwnProperty(call FunctionCall) Value {
propertyName := toString(call.Argument(0))
propertyName := call.Argument(0).string()
thisObject := call.thisObject()
return toValue_bool(thisObject.hasOwnProperty(propertyName))
}
@ -42,27 +42,27 @@ func builtinObject_hasOwnProperty(call FunctionCall) Value {
func builtinObject_isPrototypeOf(call FunctionCall) Value {
value := call.Argument(0)
if !value.IsObject() {
return FalseValue()
return falseValue
}
prototype := call.toObject(value).prototype
thisObject := call.thisObject()
for prototype != nil {
if thisObject == prototype {
return TrueValue()
return trueValue
}
prototype = prototype.prototype
}
return FalseValue()
return falseValue
}
func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
propertyName := toString(call.Argument(0))
propertyName := call.Argument(0).string()
thisObject := call.thisObject()
property := thisObject.getOwnProperty(propertyName)
if property != nil && property.enumerable() {
return TrueValue()
return trueValue
}
return FalseValue()
return falseValue
}
func builtinObject_toString(call FunctionCall) Value {
@ -80,20 +80,20 @@ func builtinObject_toString(call FunctionCall) Value {
func builtinObject_toLocaleString(call FunctionCall) Value {
toString := call.thisObject().get("toString")
if !toString.isCallable() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
return toString.call(call.This)
return toString.call(call.runtime, call.This)
}
func builtinObject_getPrototypeOf(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
if object.prototype == nil {
return NullValue()
return nullValue
}
return toValue_object(object.prototype)
@ -103,13 +103,13 @@ func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
name := toString(call.Argument(1))
name := call.Argument(1).string()
descriptor := object.getOwnProperty(name)
if descriptor == nil {
return UndefinedValue()
return Value{}
}
return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor))
}
@ -118,10 +118,10 @@ func builtinObject_defineProperty(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
name := toString(call.Argument(1))
descriptor := toPropertyDescriptor(call.Argument(2))
name := call.Argument(1).string()
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
object.defineOwnProperty(name, descriptor, true)
return objectValue
}
@ -130,12 +130,12 @@ func builtinObject_defineProperties(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
properties := call.runtime.toObject(call.Argument(1))
properties.enumerate(false, func(name string) bool {
descriptor := toPropertyDescriptor(properties.get(name))
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
object.defineOwnProperty(name, descriptor, true)
return true
})
@ -146,7 +146,7 @@ func builtinObject_defineProperties(call FunctionCall) Value {
func builtinObject_create(call FunctionCall) Value {
prototypeValue := call.Argument(0)
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
object := call.runtime.newObject()
@ -156,7 +156,7 @@ func builtinObject_create(call FunctionCall) Value {
if propertiesValue.IsDefined() {
properties := call.runtime.toObject(propertiesValue)
properties.enumerate(false, func(name string) bool {
descriptor := toPropertyDescriptor(properties.get(name))
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
object.defineOwnProperty(name, descriptor, true)
return true
})
@ -170,7 +170,7 @@ func builtinObject_isExtensible(call FunctionCall) Value {
if object := object._object(); object != nil {
return toValue_bool(object.extensible)
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinObject_preventExtensions(call FunctionCall) Value {
@ -178,7 +178,7 @@ func builtinObject_preventExtensions(call FunctionCall) Value {
if object := object._object(); object != nil {
object.extensible = false
} else {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
return object
}
@ -199,7 +199,7 @@ func builtinObject_isSealed(call FunctionCall) Value {
})
return toValue_bool(result)
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinObject_seal(call FunctionCall) Value {
@ -214,7 +214,7 @@ func builtinObject_seal(call FunctionCall) Value {
})
object.extensible = false
} else {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
return object
}
@ -235,7 +235,7 @@ func builtinObject_isFrozen(call FunctionCall) Value {
})
return toValue_bool(result)
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinObject_freeze(call FunctionCall) Value {
@ -259,7 +259,7 @@ func builtinObject_freeze(call FunctionCall) Value {
})
object.extensible = false
} else {
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
return object
}
@ -272,7 +272,7 @@ func builtinObject_keys(call FunctionCall) Value {
})
return toValue_object(call.runtime.newArrayOf(keys))
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}
func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
@ -285,5 +285,5 @@ func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
})
return toValue_object(call.runtime.newArrayOf(propertyNames))
}
panic(newTypeError())
panic(call.runtime.panicTypeError())
}

View file

@ -17,7 +17,7 @@ func builtinRegExp(call FunctionCall) Value {
return toValue_object(call.runtime.newRegExp(pattern, flags))
}
func builtinNewRegExp(self *_object, _ Value, argumentList []Value) Value {
func builtinNewRegExp(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newRegExp(
valueOfArrayIndex(argumentList, 0),
valueOfArrayIndex(argumentList, 1),
@ -26,15 +26,15 @@ func builtinNewRegExp(self *_object, _ Value, argumentList []Value) Value {
func builtinRegExp_toString(call FunctionCall) Value {
thisObject := call.thisObject()
source := toString(thisObject.get("source"))
source := thisObject.get("source").string()
flags := []byte{}
if toBoolean(thisObject.get("global")) {
if thisObject.get("global").bool() {
flags = append(flags, 'g')
}
if toBoolean(thisObject.get("ignoreCase")) {
if thisObject.get("ignoreCase").bool() {
flags = append(flags, 'i')
}
if toBoolean(thisObject.get("multiline")) {
if thisObject.get("multiline").bool() {
flags = append(flags, 'm')
}
return toValue_string(fmt.Sprintf("/%s/%s", source, flags))
@ -42,17 +42,17 @@ func builtinRegExp_toString(call FunctionCall) Value {
func builtinRegExp_exec(call FunctionCall) Value {
thisObject := call.thisObject()
target := toString(call.Argument(0))
target := call.Argument(0).string()
match, result := execRegExp(thisObject, target)
if !match {
return NullValue()
return nullValue
}
return toValue_object(execResultToArray(call.runtime, target, result))
}
func builtinRegExp_test(call FunctionCall) Value {
thisObject := call.thisObject()
target := toString(call.Argument(0))
target := call.Argument(0).string()
match, _ := execRegExp(thisObject, target)
return toValue_bool(match)
}
@ -61,5 +61,5 @@ 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()
return Value{}
}

View file

@ -12,7 +12,7 @@ import (
func stringValueFromStringArgumentList(argumentList []Value) Value {
if len(argumentList) > 0 {
return toValue_string(toString(argumentList[0]))
return toValue_string(argumentList[0].string())
}
return toValue_string("")
}
@ -21,7 +21,7 @@ func builtinString(call FunctionCall) Value {
return stringValueFromStringArgumentList(call.ArgumentList)
}
func builtinNewString(self *_object, _ Value, argumentList []Value) Value {
func builtinNewString(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
}
@ -41,8 +41,8 @@ func builtinString_fromCharCode(call FunctionCall) Value {
}
func builtinString_charAt(call FunctionCall) Value {
checkObjectCoercible(call.This)
idx := int(toInteger(call.Argument(0)).value)
checkObjectCoercible(call.runtime, call.This)
idx := int(call.Argument(0).number().int64)
chr := stringAt(call.This._object().stringValue(), idx)
if chr == utf8.RuneError {
return toValue_string("")
@ -51,8 +51,8 @@ func builtinString_charAt(call FunctionCall) Value {
}
func builtinString_charCodeAt(call FunctionCall) Value {
checkObjectCoercible(call.This)
idx := int(toInteger(call.Argument(0)).value)
checkObjectCoercible(call.runtime, call.This)
idx := int(call.Argument(0).number().int64)
chr := stringAt(call.This._object().stringValue(), idx)
if chr == utf8.RuneError {
return NaNValue()
@ -61,19 +61,19 @@ func builtinString_charCodeAt(call FunctionCall) Value {
}
func builtinString_concat(call FunctionCall) Value {
checkObjectCoercible(call.This)
checkObjectCoercible(call.runtime, call.This)
var value bytes.Buffer
value.WriteString(toString(call.This))
value.WriteString(call.This.string())
for _, item := range call.ArgumentList {
value.WriteString(toString(item))
value.WriteString(item.string())
}
return toValue_string(value.String())
}
func builtinString_indexOf(call FunctionCall) Value {
checkObjectCoercible(call.This)
value := toString(call.This)
target := toString(call.Argument(0))
checkObjectCoercible(call.runtime, call.This)
value := call.This.string()
target := call.Argument(0).string()
if 2 > len(call.ArgumentList) {
return toValue_int(strings.Index(value, target))
}
@ -94,9 +94,9 @@ func builtinString_indexOf(call FunctionCall) Value {
}
func builtinString_lastIndexOf(call FunctionCall) Value {
checkObjectCoercible(call.This)
value := toString(call.This)
target := toString(call.Argument(0))
checkObjectCoercible(call.runtime, call.This)
value := call.This.string()
target := call.Argument(0).string()
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
return toValue_int(strings.LastIndex(value, target))
}
@ -104,15 +104,15 @@ func builtinString_lastIndexOf(call FunctionCall) Value {
if length == 0 {
return toValue_int(strings.LastIndex(value, target))
}
start := toInteger(call.ArgumentList[1])
if !start.valid() {
start := call.ArgumentList[1].number()
if start.kind == numberInfinity { // FIXME
// startNumber is infinity, so start is the end of string (start = length)
return toValue_int(strings.LastIndex(value, target))
}
if 0 > start.value {
start.value = 0
if 0 > start.int64 {
start.int64 = 0
}
end := int(start.value) + len(target)
end := int(start.int64) + len(target)
if end > length {
end = length
}
@ -120,18 +120,18 @@ func builtinString_lastIndexOf(call FunctionCall) Value {
}
func builtinString_match(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := toString(call.This)
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
matcherValue := call.Argument(0)
matcher := matcherValue._object()
if !matcherValue.IsObject() || matcher.class != "RegExp" {
matcher = call.runtime.newRegExp(matcherValue, UndefinedValue())
matcher = call.runtime.newRegExp(matcherValue, Value{})
}
global := toBoolean(matcher.get("global"))
global := matcher.get("global").bool()
if !global {
match, result := execRegExp(matcher, target)
if !match {
return NullValue()
return nullValue
}
return toValue_object(execResultToArray(call.runtime, target, result))
}
@ -141,7 +141,7 @@ func builtinString_match(call FunctionCall) Value {
matchCount := len(result)
if result == nil {
matcher.put("lastIndex", toValue_int(0), true)
return UndefinedValue() // !match
return Value{} // !match
}
matchCount = len(result)
valueArray := make([]Value, matchCount)
@ -189,8 +189,8 @@ func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int
}
func builtinString_replace(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := []byte(toString(call.This))
checkObjectCoercible(call.runtime, call.This)
target := []byte(call.This.string())
searchValue := call.Argument(0)
searchObject := searchValue._object()
@ -205,7 +205,7 @@ func builtinString_replace(call FunctionCall) Value {
find = -1
}
} else {
search = regexp.MustCompile(regexp.QuoteMeta(toString(searchValue)))
search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string()))
}
found := search.FindAllSubmatchIndex(target, find)
@ -232,18 +232,18 @@ func builtinString_replace(call FunctionCall) Value {
if match[offset] != -1 {
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
} else {
argumentList[index] = UndefinedValue()
argumentList[index] = Value{}
}
}
argumentList[matchCount+0] = toValue_int(match[0])
argumentList[matchCount+1] = toValue_string(target)
replacement := toString(replace.Call(UndefinedValue(), argumentList))
replacement := replace.call(Value{}, argumentList, false, nativeFrame).string()
result = append(result, []byte(replacement)...)
lastIndex = match[1]
}
} else {
replace := []byte(toString(replaceValue))
replace := []byte(replaceValue.string())
for _, match := range found {
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
lastIndex = match[1]
@ -260,17 +260,15 @@ func builtinString_replace(call FunctionCall) Value {
return toValue_string(string(result))
}
return UndefinedValue()
}
func builtinString_search(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := toString(call.This)
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
searchValue := call.Argument(0)
search := searchValue._object()
if !searchValue.IsObject() || search.class != "RegExp" {
search = call.runtime.newRegExp(searchValue, UndefinedValue())
search = call.runtime.newRegExp(searchValue, Value{})
}
result := search.regExpValue().regularExpression.FindStringIndex(target)
if result == nil {
@ -291,8 +289,8 @@ func stringSplitMatch(target string, targetLength int64, index uint, search stri
}
func builtinString_split(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := toString(call.This)
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
separatorValue := call.Argument(0)
limitValue := call.Argument(1)
@ -343,7 +341,7 @@ func builtinString_split(call FunctionCall) Value {
captureCount := len(match) / 2
for index := 1; index < captureCount; index++ {
offset := index * 2
value := UndefinedValue()
value := Value{}
if match[offset] != -1 {
value = toValue_string(target[match[offset]:match[offset+1]])
}
@ -367,7 +365,7 @@ func builtinString_split(call FunctionCall) Value {
return toValue_object(call.runtime.newArrayOf(valueArray))
} else {
separator := toString(separatorValue)
separator := separatorValue.string()
splitLimit := limit
excess := false
@ -389,13 +387,11 @@ func builtinString_split(call FunctionCall) Value {
return toValue_object(call.runtime.newArrayOf(valueArray))
}
return UndefinedValue()
}
func builtinString_slice(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := toString(call.This)
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
length := int64(len(target))
start, end := rangeStartEnd(call.ArgumentList, length, false)
@ -406,8 +402,8 @@ func builtinString_slice(call FunctionCall) Value {
}
func builtinString_substring(call FunctionCall) Value {
checkObjectCoercible(call.This)
target := toString(call.This)
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
length := int64(len(target))
start, end := rangeStartEnd(call.ArgumentList, length, true)
@ -418,7 +414,7 @@ func builtinString_substring(call FunctionCall) Value {
}
func builtinString_substr(call FunctionCall) Value {
target := toString(call.This)
target := call.This.string()
size := int64(len(target))
start, length := rangeStartLength(call.ArgumentList, size)
@ -443,42 +439,42 @@ func builtinString_substr(call FunctionCall) Value {
}
func builtinString_toLowerCase(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue_string(strings.ToLower(toString(call.This)))
checkObjectCoercible(call.runtime, call.This)
return toValue_string(strings.ToLower(call.This.string()))
}
func builtinString_toUpperCase(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue_string(strings.ToUpper(toString(call.This)))
checkObjectCoercible(call.runtime, call.This)
return toValue_string(strings.ToUpper(call.This.string()))
}
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
func builtinString_trim(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue(strings.Trim(toString(call.This),
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.Trim(call.This.string(),
builtinString_trim_whitespace))
}
// Mozilla extension, not ECMAScript 5
func builtinString_trimLeft(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue(strings.TrimLeft(toString(call.This),
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.TrimLeft(call.This.string(),
builtinString_trim_whitespace))
}
// Mozilla extension, not ECMAScript 5
func builtinString_trimRight(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue(strings.TrimRight(toString(call.This),
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.TrimRight(call.This.string(),
builtinString_trim_whitespace))
}
func builtinString_localeCompare(call FunctionCall) Value {
checkObjectCoercible(call.This)
this := toString(call.This)
that := toString(call.Argument(0))
checkObjectCoercible(call.runtime, call.This)
this := call.This.string()
that := call.Argument(0).string()
if this < that {
return toValue_int(-1)
} else if this == that {
@ -491,7 +487,7 @@ func builtinString_localeCompare(call FunctionCall) Value {
An alternate version of String.trim
func builtinString_trim(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue_string(strings.TrimFunc(toString(call.This), isWhiteSpaceOrLineTerminator))
return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator))
}
*/

View file

@ -0,0 +1,155 @@
package otto
import (
"fmt"
)
type _clone struct {
runtime *_runtime
_object map[*_object]*_object
_objectStash map[*_objectStash]*_objectStash
_dclStash map[*_dclStash]*_dclStash
_fnStash map[*_fnStash]*_fnStash
}
func (in *_runtime) clone() *_runtime {
in.lck.Lock()
defer in.lck.Unlock()
out := &_runtime{}
clone := _clone{
runtime: out,
_object: make(map[*_object]*_object),
_objectStash: make(map[*_objectStash]*_objectStash),
_dclStash: make(map[*_dclStash]*_dclStash),
_fnStash: make(map[*_fnStash]*_fnStash),
}
globalObject := clone.object(in.globalObject)
out.globalStash = out.newObjectStash(globalObject, nil)
out.globalObject = globalObject
out.global = _global{
clone.object(in.global.Object),
clone.object(in.global.Function),
clone.object(in.global.Array),
clone.object(in.global.String),
clone.object(in.global.Boolean),
clone.object(in.global.Number),
clone.object(in.global.Math),
clone.object(in.global.Date),
clone.object(in.global.RegExp),
clone.object(in.global.Error),
clone.object(in.global.EvalError),
clone.object(in.global.TypeError),
clone.object(in.global.RangeError),
clone.object(in.global.ReferenceError),
clone.object(in.global.SyntaxError),
clone.object(in.global.URIError),
clone.object(in.global.JSON),
clone.object(in.global.ObjectPrototype),
clone.object(in.global.FunctionPrototype),
clone.object(in.global.ArrayPrototype),
clone.object(in.global.StringPrototype),
clone.object(in.global.BooleanPrototype),
clone.object(in.global.NumberPrototype),
clone.object(in.global.DatePrototype),
clone.object(in.global.RegExpPrototype),
clone.object(in.global.ErrorPrototype),
clone.object(in.global.EvalErrorPrototype),
clone.object(in.global.TypeErrorPrototype),
clone.object(in.global.RangeErrorPrototype),
clone.object(in.global.ReferenceErrorPrototype),
clone.object(in.global.SyntaxErrorPrototype),
clone.object(in.global.URIErrorPrototype),
}
out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object)
out.globalObject.prototype = out.global.ObjectPrototype
// Not sure if this is necessary, but give some help to the GC
clone.runtime = nil
clone._object = nil
clone._objectStash = nil
clone._dclStash = nil
clone._fnStash = nil
return out
}
func (clone *_clone) object(in *_object) *_object {
if out, exists := clone._object[in]; exists {
return out
}
out := &_object{}
clone._object[in] = out
return in.objectClass.clone(in, out, clone)
}
func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) {
if out, exists := clone._dclStash[in]; exists {
return out, true
}
out := &_dclStash{}
clone._dclStash[in] = out
return out, false
}
func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) {
if out, exists := clone._objectStash[in]; exists {
return out, true
}
out := &_objectStash{}
clone._objectStash[in] = out
return out, false
}
func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) {
if out, exists := clone._fnStash[in]; exists {
return out, true
}
out := &_fnStash{}
clone._fnStash[in] = out
return out, false
}
func (clone *_clone) value(in Value) Value {
out := in
switch value := in.value.(type) {
case *_object:
out.value = clone.object(value)
}
return out
}
func (clone *_clone) valueArray(in []Value) []Value {
out := make([]Value, len(in))
for index, value := range in {
out[index] = clone.value(value)
}
return out
}
func (clone *_clone) stash(in _stash) _stash {
if in == nil {
return nil
}
return in.clone(clone)
}
func (clone *_clone) property(in _property) _property {
out := in
if value, valid := in.value.(Value); valid {
out.value = clone.value(value)
} else {
panic(fmt.Errorf("in.value.(Value) != true"))
}
return out
}
func (clone *_clone) dclProperty(in _dclProperty) _dclProperty {
out := in
out.value = clone.value(in.value)
return out
}

View file

@ -0,0 +1,24 @@
package otto
import (
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/file"
)
type _file struct {
name string
src string
base int // This will always be 1 or greater
}
type _compiler struct {
file *file.File
program *ast.Program
}
func (cmpl *_compiler) parse() *_nodeProgram {
if cmpl.program != nil {
cmpl.file = cmpl.program.File
}
return cmpl._parse(cmpl.program)
}

View file

@ -4,13 +4,20 @@ import (
"strconv"
)
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram) Value {
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value {
if !eval {
self.enterGlobalScope()
defer func() {
self.leaveScope()
}()
}
self.cmpl_functionDeclaration(node.functionList)
self.cmpl_variableDeclaration(node.varList)
self.scope.frame.file = node.file
return self.cmpl_evaluate_nodeStatementList(node.body)
}
func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_functionEnvironment, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
indexOfParameterName := make([]string, len(argumentList))
// function(abc, def, ghi)
@ -24,19 +31,21 @@ func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_fu
if name == "arguments" {
argumentsFound = true
}
value := UndefinedValue()
value := Value{}
if index < len(argumentList) {
value = argumentList[index]
indexOfParameterName[index] = name
}
self.localSet(name, value)
// strict = false
self.scope.lexical.setValue(name, value, false)
}
if !argumentsFound {
arguments := self.newArgumentsObject(indexOfParameterName, environment, len(argumentList))
arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList))
arguments.defineProperty("callee", toValue_object(function), 0101, false)
environment.arguments = arguments
self.localSet("arguments", toValue_object(arguments))
stash.arguments = arguments
// strict = false
self.scope.lexical.setValue("arguments", toValue_object(arguments), false)
for index, _ := range argumentList {
if index < len(node.parameterList) {
continue
@ -50,38 +59,38 @@ func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_fu
self.cmpl_variableDeclaration(node.varList)
result := self.cmpl_evaluate_nodeStatement(node.body)
if result.isResult() {
if result.kind == valueResult {
return result
}
return UndefinedValue()
return Value{}
}
func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) {
executionContext := self._executionContext(0)
executionContext := self.scope
eval := executionContext.eval
environment := executionContext.VariableEnvironment
stash := executionContext.variable
for _, function := range list {
name := function.name
value := self.cmpl_evaluate_nodeExpression(function)
if !environment.HasBinding(name) {
environment.CreateMutableBinding(name, eval == true)
}
if !stash.hasBinding(name) {
stash.createBinding(name, eval == true, value)
} else {
// TODO 10.5.5.e
environment.SetMutableBinding(name, value, false) // TODO strict
stash.setBinding(name, value, false) // TODO strict
}
}
}
func (self *_runtime) cmpl_variableDeclaration(list []string) {
executionContext := self._executionContext(0)
executionContext := self.scope
eval := executionContext.eval
environment := executionContext.VariableEnvironment
stash := executionContext.variable
for _, name := range list {
if !environment.HasBinding(name) {
environment.CreateMutableBinding(name, eval == true)
environment.SetMutableBinding(name, UndefinedValue(), false) // TODO strict
if !stash.hasBinding(name) {
stash.createBinding(name, eval == true, Value{}) // TODO strict?
}
}
}

View file

@ -13,10 +13,10 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
// If the Interrupt channel is nil, then
// we avoid runtime.Gosched() overhead (if any)
// FIXME: Test this
if self.Otto.Interrupt != nil {
if self.otto.Interrupt != nil {
runtime.Gosched()
select {
case value := <-self.Otto.Interrupt:
case value := <-self.otto.Interrupt:
value()
default:
}
@ -50,15 +50,14 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
return self.cmpl_evaluate_nodeDotExpression(node)
case *_nodeFunctionLiteral:
var local = self.LexicalEnvironment()
var local = self.scope.lexical
if node.name != "" {
local = self.newDeclarativeEnvironment(local)
local = self.newDeclarationStash(local)
}
value := toValue_object(self.newNodeFunction(node, local))
if node.name != "" {
local.CreateMutableBinding(node.name, false)
local.SetMutableBinding(node.name, value, false)
local.createBinding(node.name, false, value)
}
return value
@ -67,7 +66,7 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
// 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)
reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx))
if reference == nil {
// Should never get here!
panic(hereBeDragons("referenceError == nil: " + name))
@ -90,7 +89,7 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
return self.cmpl_evaluate_nodeSequenceExpression(node)
case *_nodeThisExpression:
return toValue_object(self._executionContext(0).this)
return toValue_object(self.scope.this)
case *_nodeUnaryExpression:
return self.cmpl_evaluate_nodeUnaryExpression(node)
@ -108,9 +107,9 @@ func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Va
for _, node := range node.value {
if node == nil {
valueArray = append(valueArray, Value{})
valueArray = append(valueArray, emptyValue)
} else {
valueArray = append(valueArray, self.GetValue(self.cmpl_evaluate_nodeExpression(node)))
valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve())
}
}
@ -123,14 +122,14 @@ func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpres
left := self.cmpl_evaluate_nodeExpression(node.left)
right := self.cmpl_evaluate_nodeExpression(node.right)
rightValue := self.GetValue(right)
rightValue := right.resolve()
result := rightValue
if node.operator != token.ASSIGN {
result = self.calculateBinaryExpression(node.operator, left, rightValue)
}
self.PutValue(left.reference(), result)
self.putValue(left.reference(), result)
return result
}
@ -138,22 +137,22 @@ func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpres
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value {
left := self.cmpl_evaluate_nodeExpression(node.left)
leftValue := self.GetValue(left)
leftValue := left.resolve()
switch node.operator {
// Logical
case token.LOGICAL_AND:
if !toBoolean(leftValue) {
if !leftValue.bool() {
return leftValue
}
right := self.cmpl_evaluate_nodeExpression(node.right)
return self.GetValue(right)
return right.resolve()
case token.LOGICAL_OR:
if toBoolean(leftValue) {
if leftValue.bool() {
return leftValue
}
right := self.cmpl_evaluate_nodeExpression(node.right)
return self.GetValue(right)
return right.resolve()
}
return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right))
@ -161,57 +160,90 @@ func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpres
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))
left := self.cmpl_evaluate_nodeExpression(node.left).resolve()
right := self.cmpl_evaluate_nodeExpression(node.right).resolve()
return toValue_bool(self.calculateComparison(node.operator, left, right))
}
func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value {
target := self.cmpl_evaluate_nodeExpression(node.left)
targetValue := self.GetValue(target)
targetValue := target.resolve()
member := self.cmpl_evaluate_nodeExpression(node.member)
memberValue := self.GetValue(member)
memberValue := member.resolve()
// TODO Pass in base value as-is, and defer toObject till later?
return toValue(newPropertyReference(self.toObject(targetValue), toString(memberValue), false))
return toValue(newPropertyReference(self, self.toObject(targetValue), memberValue.string(), false, _at(node.idx)))
}
func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value {
rt := self
this := Value{}
callee := self.cmpl_evaluate_nodeExpression(node.callee)
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)))
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
}
}
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
rf := callee.reference()
vl := callee.resolve()
eval := false // Whether this call is a (candidate for) direct call to eval
name := ""
if rf != nil {
switch rf := rf.(type) {
case *_propertyReference:
name = rf.name
object := rf.base
this = toValue_object(object)
eval = rf.name == "eval" // Possible direct eval
case *_stashReference:
// TODO ImplicitThisValue
name = rf.name
eval = rf.name == "eval" // Possible direct eval
default:
// FIXME?
panic(rt.panicTypeError("Here be dragons"))
}
}
if !calleeValue.IsFunction() {
panic(newTypeError("%v is not a function", calleeValue))
at := _at(-1)
switch callee := node.callee.(type) {
case *_nodeIdentifier:
at = _at(callee.idx)
case *_nodeDotExpression:
at = _at(callee.idx)
case *_nodeBracketExpression:
at = _at(callee.idx)
}
return self.Call(calleeValue._object(), this, argumentList, evalHint)
frame := _frame{
callee: name,
file: self.scope.frame.file,
}
if !vl.IsFunction() {
if name == "" {
// FIXME Maybe typeof?
panic(rt.panicTypeError("%v is not a function", vl, at))
}
panic(rt.panicTypeError("'%s' is not a function", name, at))
}
self.scope.frame.offset = int(at)
return vl._object().call(this, argumentList, eval, frame)
}
func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value {
test := self.cmpl_evaluate_nodeExpression(node.test)
testValue := self.GetValue(test)
if toBoolean(testValue) {
testValue := test.resolve()
if testValue.bool() {
return self.cmpl_evaluate_nodeExpression(node.consequent)
}
return self.cmpl_evaluate_nodeExpression(node.alternate)
@ -219,27 +251,60 @@ func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditi
func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value {
target := self.cmpl_evaluate_nodeExpression(node.left)
targetValue := self.GetValue(target)
targetValue := target.resolve()
// 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())))
panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error()))
}
return toValue(newPropertyReference(object, node.identifier, false))
return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx)))
}
func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value {
rt := self
callee := self.cmpl_evaluate_nodeExpression(node.callee)
calleeValue := self.GetValue(callee)
argumentList := []Value{}
for _, argumentNode := range node.argumentList {
argumentList = append(argumentList, self.GetValue(self.cmpl_evaluate_nodeExpression(argumentNode)))
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
}
this := UndefinedValue()
if !calleeValue.IsFunction() {
panic(newTypeError("%v is not a function", calleeValue))
rf := callee.reference()
vl := callee.resolve()
name := ""
if rf != nil {
switch rf := rf.(type) {
case *_propertyReference:
name = rf.name
case *_stashReference:
name = rf.name
default:
panic(rt.panicTypeError("Here be dragons"))
}
return calleeValue._object().Construct(this, argumentList)
}
at := _at(-1)
switch callee := node.callee.(type) {
case *_nodeIdentifier:
at = _at(callee.idx)
case *_nodeDotExpression:
at = _at(callee.idx)
case *_nodeBracketExpression:
at = _at(callee.idx)
}
if !vl.IsFunction() {
if name == "" {
// FIXME Maybe typeof?
panic(rt.panicTypeError("%v is not a function", vl, at))
}
panic(rt.panicTypeError("'%s' is not a function", name, at))
}
self.scope.frame.offset = int(at)
return vl._object().construct(argumentList)
}
func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value {
@ -249,15 +314,15 @@ func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral)
for _, property := range node.value {
switch property.kind {
case "value":
result.defineProperty(property.key, self.GetValue(self.cmpl_evaluate_nodeExpression(property.value)), 0111, false)
result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false)
case "get":
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.LexicalEnvironment())
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
descriptor := _property{}
descriptor.mode = 0211
descriptor.value = _propertyGetSet{getter, nil}
result.defineOwnProperty(property.key, descriptor, false)
case "set":
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.LexicalEnvironment())
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
descriptor := _property{}
descriptor.mode = 0211
descriptor.value = _propertyGetSet{nil, setter}
@ -274,7 +339,7 @@ func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceEx
var result Value
for _, node := range node.sequence {
result = self.cmpl_evaluate_nodeExpression(node)
result = self.GetValue(result)
result = result.resolve()
}
return result
}
@ -284,31 +349,31 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
target := self.cmpl_evaluate_nodeExpression(node.operand)
switch node.operator {
case token.TYPEOF, token.DELETE:
if target._valueType == valueReference && target.reference().IsUnresolvable() {
if target.kind == valueReference && target.reference().invalid() {
if node.operator == token.TYPEOF {
return toValue_string("undefined")
}
return TrueValue()
return trueValue
}
}
switch node.operator {
case token.NOT:
targetValue := self.GetValue(target)
if targetValue.toBoolean() {
return FalseValue()
targetValue := target.resolve()
if targetValue.bool() {
return falseValue
}
return TrueValue()
return trueValue
case token.BITWISE_NOT:
targetValue := self.GetValue(target)
targetValue := target.resolve()
integerValue := toInt32(targetValue)
return toValue_int32(^integerValue)
case token.PLUS:
targetValue := self.GetValue(target)
return toValue_float64(targetValue.toFloat())
targetValue := target.resolve()
return toValue_float64(targetValue.float64())
case token.MINUS:
targetValue := self.GetValue(target)
value := targetValue.toFloat()
targetValue := target.resolve()
value := targetValue.float64()
// TODO Test this
sign := float64(-1)
if math.Signbit(value) {
@ -316,45 +381,45 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
}
return toValue_float64(math.Copysign(value, sign))
case token.INCREMENT:
targetValue := self.GetValue(target)
targetValue := target.resolve()
if node.postfix {
// Postfix++
oldValue := targetValue.toFloat()
oldValue := targetValue.float64()
newValue := toValue_float64(+1 + oldValue)
self.PutValue(target.reference(), newValue)
self.putValue(target.reference(), newValue)
return toValue_float64(oldValue)
} else {
// ++Prefix
newValue := toValue_float64(+1 + targetValue.toFloat())
self.PutValue(target.reference(), newValue)
newValue := toValue_float64(+1 + targetValue.float64())
self.putValue(target.reference(), newValue)
return newValue
}
case token.DECREMENT:
targetValue := self.GetValue(target)
targetValue := target.resolve()
if node.postfix {
// Postfix--
oldValue := targetValue.toFloat()
oldValue := targetValue.float64()
newValue := toValue_float64(-1 + oldValue)
self.PutValue(target.reference(), newValue)
self.putValue(target.reference(), newValue)
return toValue_float64(oldValue)
} else {
// --Prefix
newValue := toValue_float64(-1 + targetValue.toFloat())
self.PutValue(target.reference(), newValue)
newValue := toValue_float64(-1 + targetValue.float64())
self.putValue(target.reference(), newValue)
return newValue
}
case token.VOID:
self.GetValue(target) // FIXME Side effect?
return UndefinedValue()
target.resolve() // FIXME Side effect?
return Value{}
case token.DELETE:
reference := target.reference()
if reference == nil {
return TrueValue()
return trueValue
}
return toValue_bool(target.reference().Delete())
return toValue_bool(target.reference().delete())
case token.TYPEOF:
targetValue := self.GetValue(target)
switch targetValue._valueType {
targetValue := target.resolve()
switch targetValue.kind {
case valueUndefined:
return toValue_string("undefined")
case valueNull:
@ -366,7 +431,7 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
case valueString:
return toValue_string("string")
case valueObject:
if targetValue._object().functionValue().call != nil {
if targetValue._object().isCall() {
return toValue_string("function")
}
return toValue_string("object")
@ -381,11 +446,11 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
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)
left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx))
right := self.cmpl_evaluate_nodeExpression(node.initializer)
rightValue := self.GetValue(right)
rightValue := right.resolve()
self.PutValue(left, rightValue)
self.putValue(left, rightValue)
}
return toValue_string(node.name)
}

View file

@ -12,10 +12,10 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
// If the Interrupt channel is nil, then
// we avoid runtime.Gosched() overhead (if any)
// FIXME: Test this
if self.Otto.Interrupt != nil {
if self.otto.Interrupt != nil {
runtime.Gosched()
select {
case value := <-self.Otto.Interrupt:
case value := <-self.otto.Interrupt:
value()
default:
}
@ -24,8 +24,18 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
switch node := node.(type) {
case *_nodeBlockStatement:
// FIXME If result is break, then return the empty value?
return self.cmpl_evaluate_nodeStatementList(node.list)
labels := self.labels
self.labels = nil
value := self.cmpl_evaluate_nodeStatementList(node.list)
switch value.kind {
case valueResult:
switch value.evaluateBreak(labels) {
case resultBreak:
return emptyValue
}
}
return value
case *_nodeBranchStatement:
target := node.label
@ -37,13 +47,13 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
}
case *_nodeDebuggerStatement:
return Value{} // Nothing happens.
return emptyValue // Nothing happens.
case *_nodeDoWhileStatement:
return self.cmpl_evaluate_nodeDoWhileStatement(node)
case *_nodeEmptyStatement:
return Value{}
return emptyValue
case *_nodeExpressionStatement:
return self.cmpl_evaluate_nodeExpression(node.expression)
@ -70,15 +80,15 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
case *_nodeReturnStatement:
if node.argument != nil {
return toValue(newReturnResult(self.GetValue(self.cmpl_evaluate_nodeExpression(node.argument))))
return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve()))
}
return toValue(newReturnResult(UndefinedValue()))
return toValue(newReturnResult(Value{}))
case *_nodeSwitchStatement:
return self.cmpl_evaluate_nodeSwitchStatement(node)
case *_nodeThrowStatement:
value := self.GetValue(self.cmpl_evaluate_nodeExpression(node.argument))
value := self.cmpl_evaluate_nodeExpression(node.argument).resolve()
panic(newException(value))
case *_nodeTryStatement:
@ -89,7 +99,7 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
for _, variable := range node.list {
self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression))
}
return Value{}
return emptyValue
case *_nodeWhileStatement:
return self.cmpl_evaluate_nodeWhileStatement(node)
@ -106,17 +116,17 @@ func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Val
var result Value
for _, node := range list {
value := self.cmpl_evaluate_nodeStatement(node)
switch value._valueType {
switch value.kind {
case valueResult:
return value
case valueEmpty:
default:
// We have GetValue here to (for example) trigger a
// 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)
result = value.resolve()
}
}
return result
@ -129,12 +139,12 @@ func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileState
test := node.test
result := Value{}
result := emptyValue
resultBreak:
for {
for _, node := range node.body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value._valueType {
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
@ -150,7 +160,7 @@ resultBreak:
}
}
resultContinue:
if !self.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() {
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
// Stahp: do ... while (false)
break
}
@ -164,11 +174,11 @@ func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement
self.labels = nil
source := self.cmpl_evaluate_nodeExpression(node.source)
sourceValue := self.GetValue(source)
sourceValue := source.resolve()
switch sourceValue._valueType {
switch sourceValue.kind {
case valueUndefined, valueNull:
return emptyValue()
return emptyValue
}
sourceObject := self.toObject(sourceValue)
@ -176,22 +186,22 @@ func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement
into := node.into
body := node.body
result := Value{}
result := emptyValue
object := sourceObject
for object != nil {
enumerateValue := Value{}
enumerateValue := emptyValue
object.enumerate(false, func(name string) bool {
into := self.cmpl_evaluate_nodeExpression(into)
// In the case of: for (var abc in def) ...
if into.reference() == nil {
identifier := toString(into)
identifier := into.string()
// TODO Should be true or false (strictness) depending on context
into = toValue(getIdentifierReference(self.LexicalEnvironment(), identifier, false))
into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1))
}
self.PutValue(into.reference(), toValue_string(name))
self.putValue(into.reference(), toValue_string(name))
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value._valueType {
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
@ -233,22 +243,22 @@ func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Va
if initializer != nil {
initialResult := self.cmpl_evaluate_nodeExpression(initializer)
self.GetValue(initialResult) // Side-effect trigger
initialResult.resolve() // Side-effect trigger
}
result := Value{}
result := emptyValue
resultBreak:
for {
if test != nil {
testResult := self.cmpl_evaluate_nodeExpression(test)
testResultValue := self.GetValue(testResult)
if toBoolean(testResultValue) == false {
testResultValue := testResult.resolve()
if testResultValue.bool() == false {
break
}
}
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value._valueType {
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
@ -266,7 +276,7 @@ resultBreak:
resultContinue:
if update != nil {
updateResult := self.cmpl_evaluate_nodeExpression(update)
self.GetValue(updateResult) // Side-effect trigger
updateResult.resolve() // Side-effect trigger
}
}
return result
@ -274,14 +284,14 @@ resultBreak:
func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value {
test := self.cmpl_evaluate_nodeExpression(node.test)
testValue := self.GetValue(test)
if toBoolean(testValue) {
testValue := test.resolve()
if testValue.bool() {
return self.cmpl_evaluate_nodeStatement(node.consequent)
} else if node.alternate != nil {
return self.cmpl_evaluate_nodeStatement(node.alternate)
}
return Value{}
return emptyValue
}
func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value {
@ -302,18 +312,18 @@ func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStateme
}
}
result := Value{}
result := emptyValue
if target != -1 {
for _, clause := range node.body[target:] {
for _, statement := range clause.consequent {
value := self.cmpl_evaluate_nodeStatement(statement)
switch value._valueType {
switch value.kind {
case valueResult:
switch value.evaluateBreak(labels) {
case resultReturn:
return value
case resultBreak:
return Value{}
return emptyValue
}
case valueEmpty:
default:
@ -332,14 +342,15 @@ func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Va
})
if exception && node.catch != nil {
lexicalEnvironment := self._executionContext(0).newDeclarativeEnvironment(self)
outer := self.scope.lexical
self.scope.lexical = self.newDeclarationStash(outer)
defer func() {
self._executionContext(0).LexicalEnvironment = lexicalEnvironment
self.scope.lexical = outer
}()
// TODO If necessary, convert TypeError<runtime> => TypeError
// That, is, such errors can be thrown despite not being JavaScript "native"
self.localSet(node.catch.parameter, tryCatchValue)
// strict = false
self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false)
// FIXME node.CatchParameter
// FIXME node.Catch
@ -350,7 +361,7 @@ func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Va
if node.finally != nil {
finallyValue := self.cmpl_evaluate_nodeStatement(node.finally)
if finallyValue.isResult() {
if finallyValue.kind == valueResult {
return finallyValue
}
}
@ -369,16 +380,16 @@ func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement
labels := append(self.labels, "")
self.labels = nil
result := Value{}
result := emptyValue
resultBreakContinue:
for {
if !self.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() {
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
// Stahp: while (false) ...
break
}
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value._valueType {
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
@ -399,11 +410,11 @@ resultBreakContinue:
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
outer := self.scope.lexical
lexical := self.newObjectStash(self.toObject(object.resolve()), outer)
self.scope.lexical = lexical
defer func() {
self._executionContext(0).LexicalEnvironment = previousLexicalEnvironment
self.scope.lexical = outer
}()
return self.cmpl_evaluate_nodeStatement(node.body)

View file

@ -5,165 +5,170 @@ import (
"regexp"
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/token"
)
var trueLiteral = &_nodeLiteral{value: toValue_bool(true)}
var falseLiteral = &_nodeLiteral{value: toValue_bool(false)}
var nullLiteral = &_nodeLiteral{value: NullValue()}
var nullLiteral = &_nodeLiteral{value: nullValue}
var emptyStatement = &_nodeEmptyStatement{}
func parseExpression(x ast.Expression) _nodeExpression {
if x == nil {
func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression {
if in == nil {
return nil
}
switch x := x.(type) {
switch in := in.(type) {
case *ast.ArrayLiteral:
y := &_nodeArrayLiteral{
value: make([]_nodeExpression, len(x.Value)),
out := &_nodeArrayLiteral{
value: make([]_nodeExpression, len(in.Value)),
}
for i, value := range x.Value {
y.value[i] = parseExpression(value)
for i, value := range in.Value {
out.value[i] = cmpl.parseExpression(value)
}
return y
return out
case *ast.AssignExpression:
return &_nodeAssignExpression{
operator: x.Operator,
left: parseExpression(x.Left),
right: parseExpression(x.Right),
operator: in.Operator,
left: cmpl.parseExpression(in.Left),
right: cmpl.parseExpression(in.Right),
}
case *ast.BinaryExpression:
return &_nodeBinaryExpression{
operator: x.Operator,
left: parseExpression(x.Left),
right: parseExpression(x.Right),
comparison: x.Comparison,
operator: in.Operator,
left: cmpl.parseExpression(in.Left),
right: cmpl.parseExpression(in.Right),
comparison: in.Comparison,
}
case *ast.BooleanLiteral:
if x.Value {
if in.Value {
return trueLiteral
}
return falseLiteral
case *ast.BracketExpression:
return &_nodeBracketExpression{
left: parseExpression(x.Left),
member: parseExpression(x.Member),
idx: in.Left.Idx0(),
left: cmpl.parseExpression(in.Left),
member: cmpl.parseExpression(in.Member),
}
case *ast.CallExpression:
y := &_nodeCallExpression{
callee: parseExpression(x.Callee),
argumentList: make([]_nodeExpression, len(x.ArgumentList)),
out := &_nodeCallExpression{
callee: cmpl.parseExpression(in.Callee),
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
}
for i, value := range x.ArgumentList {
y.argumentList[i] = parseExpression(value)
for i, value := range in.ArgumentList {
out.argumentList[i] = cmpl.parseExpression(value)
}
return y
return out
case *ast.ConditionalExpression:
return &_nodeConditionalExpression{
test: parseExpression(x.Test),
consequent: parseExpression(x.Consequent),
alternate: parseExpression(x.Alternate),
test: cmpl.parseExpression(in.Test),
consequent: cmpl.parseExpression(in.Consequent),
alternate: cmpl.parseExpression(in.Alternate),
}
case *ast.DotExpression:
return &_nodeDotExpression{
left: parseExpression(x.Left),
identifier: x.Identifier.Name,
idx: in.Left.Idx0(),
left: cmpl.parseExpression(in.Left),
identifier: in.Identifier.Name,
}
case *ast.FunctionLiteral:
name := ""
if x.Name != nil {
name = x.Name.Name
if in.Name != nil {
name = in.Name.Name
}
y := &_nodeFunctionLiteral{
out := &_nodeFunctionLiteral{
name: name,
body: parseStatement(x.Body),
source: x.Source,
body: cmpl.parseStatement(in.Body),
source: in.Source,
file: cmpl.file,
}
if x.ParameterList != nil {
list := x.ParameterList.List
y.parameterList = make([]string, len(list))
if in.ParameterList != nil {
list := in.ParameterList.List
out.parameterList = make([]string, len(list))
for i, value := range list {
y.parameterList[i] = value.Name
out.parameterList[i] = value.Name
}
}
for _, value := range x.DeclarationList {
for _, value := range in.DeclarationList {
switch value := value.(type) {
case *ast.FunctionDeclaration:
y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral))
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
case *ast.VariableDeclaration:
for _, value := range value.List {
y.varList = append(y.varList, value.Name)
out.varList = append(out.varList, value.Name)
}
default:
panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value))
}
}
return y
return out
case *ast.Identifier:
return &_nodeIdentifier{
name: x.Name,
idx: in.Idx,
name: in.Name,
}
case *ast.NewExpression:
y := &_nodeNewExpression{
callee: parseExpression(x.Callee),
argumentList: make([]_nodeExpression, len(x.ArgumentList)),
out := &_nodeNewExpression{
callee: cmpl.parseExpression(in.Callee),
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
}
for i, value := range x.ArgumentList {
y.argumentList[i] = parseExpression(value)
for i, value := range in.ArgumentList {
out.argumentList[i] = cmpl.parseExpression(value)
}
return y
return out
case *ast.NullLiteral:
return nullLiteral
case *ast.NumberLiteral:
return &_nodeLiteral{
value: toValue(x.Value),
value: toValue(in.Value),
}
case *ast.ObjectLiteral:
y := &_nodeObjectLiteral{
value: make([]_nodeProperty, len(x.Value)),
out := &_nodeObjectLiteral{
value: make([]_nodeProperty, len(in.Value)),
}
for i, value := range x.Value {
y.value[i] = _nodeProperty{
for i, value := range in.Value {
out.value[i] = _nodeProperty{
key: value.Key,
kind: value.Kind,
value: parseExpression(value.Value),
value: cmpl.parseExpression(value.Value),
}
}
return y
return out
case *ast.RegExpLiteral:
return &_nodeRegExpLiteral{
flags: x.Flags,
pattern: x.Pattern,
flags: in.Flags,
pattern: in.Pattern,
}
case *ast.SequenceExpression:
y := &_nodeSequenceExpression{
sequence: make([]_nodeExpression, len(x.Sequence)),
out := &_nodeSequenceExpression{
sequence: make([]_nodeExpression, len(in.Sequence)),
}
for i, value := range x.Sequence {
y.sequence[i] = parseExpression(value)
for i, value := range in.Sequence {
out.sequence[i] = cmpl.parseExpression(value)
}
return y
return out
case *ast.StringLiteral:
return &_nodeLiteral{
value: toValue_string(x.Value),
value: toValue_string(in.Value),
}
case *ast.ThisExpression:
@ -171,203 +176,211 @@ func parseExpression(x ast.Expression) _nodeExpression {
case *ast.UnaryExpression:
return &_nodeUnaryExpression{
operator: x.Operator,
operand: parseExpression(x.Operand),
postfix: x.Postfix,
operator: in.Operator,
operand: cmpl.parseExpression(in.Operand),
postfix: in.Postfix,
}
case *ast.VariableExpression:
return &_nodeVariableExpression{
name: x.Name,
initializer: parseExpression(x.Initializer),
idx: in.Idx0(),
name: in.Name,
initializer: cmpl.parseExpression(in.Initializer),
}
}
panic(fmt.Errorf("Here be dragons: parseExpression(%T)", x))
panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in))
}
func parseStatement(x ast.Statement) _nodeStatement {
if x == nil {
func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement {
if in == nil {
return nil
}
switch x := x.(type) {
switch in := in.(type) {
case *ast.BlockStatement:
y := &_nodeBlockStatement{
list: make([]_nodeStatement, len(x.List)),
out := &_nodeBlockStatement{
list: make([]_nodeStatement, len(in.List)),
}
for i, value := range x.List {
y.list[i] = parseStatement(value)
for i, value := range in.List {
out.list[i] = cmpl.parseStatement(value)
}
return y
return out
case *ast.BranchStatement:
y := &_nodeBranchStatement{
branch: x.Token,
out := &_nodeBranchStatement{
branch: in.Token,
}
if x.Label != nil {
y.label = x.Label.Name
if in.Label != nil {
out.label = in.Label.Name
}
return y
return out
case *ast.DebuggerStatement:
return &_nodeDebuggerStatement{}
case *ast.DoWhileStatement:
y := &_nodeDoWhileStatement{
test: parseExpression(x.Test),
out := &_nodeDoWhileStatement{
test: cmpl.parseExpression(in.Test),
}
body := parseStatement(x.Body)
body := cmpl.parseStatement(in.Body)
if block, ok := body.(*_nodeBlockStatement); ok {
y.body = block.list
out.body = block.list
} else {
y.body = append(y.body, body)
out.body = append(out.body, body)
}
return y
return out
case *ast.EmptyStatement:
return emptyStatement
case *ast.ExpressionStatement:
return &_nodeExpressionStatement{
expression: parseExpression(x.Expression),
expression: cmpl.parseExpression(in.Expression),
}
case *ast.ForInStatement:
y := &_nodeForInStatement{
into: parseExpression(x.Into),
source: parseExpression(x.Source),
out := &_nodeForInStatement{
into: cmpl.parseExpression(in.Into),
source: cmpl.parseExpression(in.Source),
}
body := parseStatement(x.Body)
body := cmpl.parseStatement(in.Body)
if block, ok := body.(*_nodeBlockStatement); ok {
y.body = block.list
out.body = block.list
} else {
y.body = append(y.body, body)
out.body = append(out.body, body)
}
return y
return out
case *ast.ForStatement:
y := &_nodeForStatement{
initializer: parseExpression(x.Initializer),
update: parseExpression(x.Update),
test: parseExpression(x.Test),
out := &_nodeForStatement{
initializer: cmpl.parseExpression(in.Initializer),
update: cmpl.parseExpression(in.Update),
test: cmpl.parseExpression(in.Test),
}
body := parseStatement(x.Body)
body := cmpl.parseStatement(in.Body)
if block, ok := body.(*_nodeBlockStatement); ok {
y.body = block.list
out.body = block.list
} else {
y.body = append(y.body, body)
out.body = append(out.body, body)
}
return y
return out
case *ast.IfStatement:
return &_nodeIfStatement{
test: parseExpression(x.Test),
consequent: parseStatement(x.Consequent),
alternate: parseStatement(x.Alternate),
test: cmpl.parseExpression(in.Test),
consequent: cmpl.parseStatement(in.Consequent),
alternate: cmpl.parseStatement(in.Alternate),
}
case *ast.LabelledStatement:
return &_nodeLabelledStatement{
label: x.Label.Name,
statement: parseStatement(x.Statement),
label: in.Label.Name,
statement: cmpl.parseStatement(in.Statement),
}
case *ast.ReturnStatement:
return &_nodeReturnStatement{
argument: parseExpression(x.Argument),
argument: cmpl.parseExpression(in.Argument),
}
case *ast.SwitchStatement:
y := &_nodeSwitchStatement{
discriminant: parseExpression(x.Discriminant),
default_: x.Default,
body: make([]*_nodeCaseStatement, len(x.Body)),
out := &_nodeSwitchStatement{
discriminant: cmpl.parseExpression(in.Discriminant),
default_: in.Default,
body: make([]*_nodeCaseStatement, len(in.Body)),
}
for i, p := range x.Body {
q := &_nodeCaseStatement{
test: parseExpression(p.Test),
consequent: make([]_nodeStatement, len(p.Consequent)),
for i, clause := range in.Body {
out.body[i] = &_nodeCaseStatement{
test: cmpl.parseExpression(clause.Test),
consequent: make([]_nodeStatement, len(clause.Consequent)),
}
for j, value := range p.Consequent {
q.consequent[j] = parseStatement(value)
for j, value := range clause.Consequent {
out.body[i].consequent[j] = cmpl.parseStatement(value)
}
y.body[i] = q
}
return y
return out
case *ast.ThrowStatement:
return &_nodeThrowStatement{
argument: parseExpression(x.Argument),
argument: cmpl.parseExpression(in.Argument),
}
case *ast.TryStatement:
y := &_nodeTryStatement{
body: parseStatement(x.Body),
finally: parseStatement(x.Finally),
out := &_nodeTryStatement{
body: cmpl.parseStatement(in.Body),
finally: cmpl.parseStatement(in.Finally),
}
if x.Catch != nil {
y.catch = &_nodeCatchStatement{
parameter: x.Catch.Parameter.Name,
body: parseStatement(x.Catch.Body),
if in.Catch != nil {
out.catch = &_nodeCatchStatement{
parameter: in.Catch.Parameter.Name,
body: cmpl.parseStatement(in.Catch.Body),
}
}
return y
return out
case *ast.VariableStatement:
y := &_nodeVariableStatement{
list: make([]_nodeExpression, len(x.List)),
out := &_nodeVariableStatement{
list: make([]_nodeExpression, len(in.List)),
}
for i, value := range x.List {
y.list[i] = parseExpression(value)
for i, value := range in.List {
out.list[i] = cmpl.parseExpression(value)
}
return y
return out
case *ast.WhileStatement:
y := &_nodeWhileStatement{
test: parseExpression(x.Test),
out := &_nodeWhileStatement{
test: cmpl.parseExpression(in.Test),
}
body := parseStatement(x.Body)
body := cmpl.parseStatement(in.Body)
if block, ok := body.(*_nodeBlockStatement); ok {
y.body = block.list
out.body = block.list
} else {
y.body = append(y.body, body)
out.body = append(out.body, body)
}
return y
return out
case *ast.WithStatement:
return &_nodeWithStatement{
object: parseExpression(x.Object),
body: parseStatement(x.Body),
object: cmpl.parseExpression(in.Object),
body: cmpl.parseStatement(in.Body),
}
}
panic(fmt.Errorf("Here be dragons: parseStatement(%T)", x))
panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in))
}
func cmpl_parse(x *ast.Program) *_nodeProgram {
y := &_nodeProgram{
body: make([]_nodeStatement, len(x.Body)),
func cmpl_parse(in *ast.Program) *_nodeProgram {
cmpl := _compiler{
program: in,
}
for i, value := range x.Body {
y.body[i] = parseStatement(value)
return cmpl.parse()
}
for _, value := range x.DeclarationList {
func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram {
out := &_nodeProgram{
body: make([]_nodeStatement, len(in.Body)),
file: in.File,
}
for i, value := range in.Body {
out.body[i] = cmpl.parseStatement(value)
}
for _, value := range in.DeclarationList {
switch value := value.(type) {
case *ast.FunctionDeclaration:
y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral))
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
case *ast.VariableDeclaration:
for _, value := range value.List {
y.varList = append(y.varList, value.Name)
out.varList = append(out.varList, value.Name)
}
default:
panic(fmt.Errorf("Here be dragons: parseProgram.DeclarationList(%T)", value))
panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value))
}
}
return y
return out
}
type _nodeProgram struct {
@ -377,6 +390,8 @@ type _nodeProgram struct {
functionList []*_nodeFunctionLiteral
variableList []_nodeDeclaration
file *file.File
}
type _nodeDeclaration struct {
@ -411,6 +426,7 @@ type (
}
_nodeBracketExpression struct {
idx file.Idx
left _nodeExpression
member _nodeExpression
}
@ -427,6 +443,7 @@ type (
}
_nodeDotExpression struct {
idx file.Idx
left _nodeExpression
identifier string
}
@ -438,9 +455,11 @@ type (
parameterList []string
varList []string
functionList []*_nodeFunctionLiteral
file *file.File
}
_nodeIdentifier struct {
idx file.Idx
name string
}
@ -483,6 +502,7 @@ type (
}
_nodeVariableExpression struct {
idx file.Idx
name string
initializer _nodeExpression
}

View file

@ -15,7 +15,7 @@ func Test_cmpl(t *testing.T) {
is(err, nil)
{
program := cmpl_parse(program)
value := vm.runtime.cmpl_evaluate_nodeProgram(program)
value := vm.runtime.cmpl_evaluate_nodeProgram(program, false)
if len(expect) > 0 {
is(value, expect[0])
}

Some files were not shown because too many files have changed in this diff Show more