console, jsre: replace otto with goja

This commit is contained in:
Guillaume Ballet 2019-06-20 12:17:15 +02:00
parent 3271a5afa0
commit 2b360f25c2
172 changed files with 19514 additions and 205492 deletions

View file

@ -20,37 +20,48 @@ import (
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"time"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/accounts/scwallet"
"github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/robertkrimen/otto"
)
// bridge is a collection of JavaScript utility methods to bride the .js runtime
// environment and the Go RPC connection backing the remote method calls.
type bridge struct {
client *rpc.Client // RPC client to execute Ethereum requests through
prompter UserPrompter // Input prompter to allow interactive user feedback
printer io.Writer // Output writer to serialize any display strings to
client *rpc.Client // RPC client to execute Ethereum requests through
prompter UserPrompter // Input prompter to allow interactive user feedback
printer io.Writer // Output writer to serialize any display strings to
runtime *goja.Runtime // Pointer to the JS runtime
}
// newBridge creates a new JavaScript wrapper around an RPC client.
func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge {
func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer, runtime *goja.Runtime) *bridge {
return &bridge{
client: client,
prompter: prompter,
printer: printer,
runtime: runtime,
}
}
func IsNumber(v goja.Value) bool {
switch v.ExportType().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
default:
return false
}
}
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
// non-echoing password prompt to acquire the passphrase and executes the original
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) NewAccount(call goja.FunctionCall) (response goja.Value) {
var (
password string
confirm string
@ -58,50 +69,58 @@ func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
)
switch {
// No password was specified, prompt the user for it
case len(call.ArgumentList) == 0:
case len(call.Arguments) == 0:
if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
if password != confirm {
throwJSException("passphrases don't match!")
throwJSException(b.runtime, "passphrases don't match!")
}
// A single string password was specified, use that
case len(call.ArgumentList) == 1 && call.Argument(0).IsString():
password, _ = call.Argument(0).ToString()
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
password = call.Argument(0).ToString().String()
// Otherwise fail with some error
default:
throwJSException("expected 0 or 1 string argument")
throwJSException(b.runtime, "expected 0 or 1 string argument")
}
// Password acquired, execute the call and return
ret, err := call.Otto.Call("jeth.newAccount", nil, password)
newAccount, callable := goja.AssertFunction(b.runtime.Get("jeth.newAccount"))
if !callable {
panic(b.runtime.ToValue("jeth.newAccount isn't callable"))
}
ret, err := newAccount(goja.Null(), b.runtime.ToValue(password))
if err != nil {
throwJSException(err.Error())
panic(b.runtime.ToValue(err.Error()))
}
return ret
}
// OpenWallet is a wrapper around personal.openWallet which can interpret and
// react to certain error messages, such as the Trezor PIN matrix request.
func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) OpenWallet(call goja.FunctionCall) (response goja.Value) {
// Make sure we have a wallet specified to open
if !call.Argument(0).IsString() {
throwJSException("first argument must be the wallet URL to open")
if call.Argument(0).ToObject(b.runtime).ClassName() != "String" {
panic(b.runtime.ToValue("first argument must be the wallet URL to open"))
}
wallet := call.Argument(0)
var passwd otto.Value
if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
passwd, _ = otto.ToValue("")
var passwd goja.Value
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
passwd = b.runtime.ToValue("")
} else {
passwd = call.Argument(1)
}
// Open the wallet and return if successful in itself
val, err := call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(b.runtime.Get("jeth.openWallet"))
if !callable {
panic(b.runtime.ToValue("jeth.openWallet is not callable"))
}
val, err := openWallet(goja.Null(), wallet, passwd)
if err == nil {
return val
}
@ -115,28 +134,28 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
}
val, err = b.readPassphraseAndReopenWallet(call)
if err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()):
// PUK input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter the pairing password: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
// PIN input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
throwJSException(b.runtime, err.Error())
}
}
}
@ -145,51 +164,55 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
// PIN unblock requested, fetch PUK and new PIN from the user
var pukpin string
if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
pukpin = input
}
if input, err := b.prompter.PromptPassword("Please enter new PIN: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
pukpin += input
}
passwd, _ = otto.ToValue(pukpin)
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
passwd = b.runtime.ToValue(pukpin)
if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
throwJSException(b.runtime, err.Error())
}
case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()):
// PIN input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
throwJSException(b.runtime, err.Error())
}
default:
// Unknown error occurred, drop to the user
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
return val
}
func (b *bridge) readPassphraseAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
func (b *bridge) readPassphraseAndReopenWallet(call goja.FunctionCall) (goja.Value, error) {
var passwd goja.Value
wallet := call.Argument(0)
if input, err := b.prompter.PromptPassword("Please enter your passphrase: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(b.runtime.Get("jeth.openWallet"))
if !callable {
return nil, fmt.Errorf("jeth.openWallet is not callable")
}
return openWallet(goja.Null(), wallet, passwd)
}
func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
func (b *bridge) readPinAndReopenWallet(call goja.FunctionCall) (goja.Value, error) {
var passwd goja.Value
wallet := call.Argument(0)
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
@ -200,52 +223,60 @@ func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, err
fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(b.runtime.Get("jeth.openWallet"))
if !callable {
return nil, fmt.Errorf("jeth.openWallet is not callable")
}
return openWallet(goja.Null(), wallet, passwd)
}
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
// uses a non-echoing password prompt to acquire the passphrase and executes the
// original RPC method (saved in jeth.unlockAccount) with it to actually execute
// the RPC call.
func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) UnlockAccount(call goja.FunctionCall) (response goja.Value) {
// Make sure we have an account specified to unlock
if !call.Argument(0).IsString() {
throwJSException("first argument must be the account to unlock")
if call.Argument(0).ExportType().Kind() != reflect.String {
throwJSException(b.runtime, "first argument must be the account to unlock")
}
account := call.Argument(0)
// If password is not given or is the null value, prompt the user for it
var passwd otto.Value
var passwd goja.Value
if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
fmt.Fprintf(b.printer, "Unlock account %s\n", account)
if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
} else {
if !call.Argument(1).IsString() {
throwJSException("password must be a string")
if call.Argument(1).ExportType().Kind() != reflect.String {
throwJSException(b.runtime, "password must be a string")
}
passwd = call.Argument(1)
}
// Third argument is the duration how long the account must be unlocked.
duration := otto.NullValue()
if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() {
if !call.Argument(2).IsNumber() {
throwJSException("unlock duration must be a number")
duration := goja.Null()
if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
if !IsNumber(call.Argument(2)) {
throwJSException(b.runtime, "unlock duration must be a number")
}
duration = call.Argument(2)
}
// Send the request to the backend and return
val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration)
unlockAccount, callable := goja.AssertFunction(b.runtime.Get("jeth.unlockAccount"))
if !callable {
throwJSException(b.runtime, "jeth.unlockAccount is not callable")
}
val, err := unlockAccount(goja.Null(), account, passwd, duration)
if err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
return val
}
@ -253,89 +284,96 @@ func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
// prompt to acquire the passphrase and executes the original RPC method (saved in
// jeth.sign) with it to actually execute the RPC call.
func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) Sign(call goja.FunctionCall) (response goja.Value) {
var (
message = call.Argument(0)
account = call.Argument(1)
passwd = call.Argument(2)
)
if !message.IsString() {
throwJSException("first argument must be the message to sign")
if message.ExportType().Kind() != reflect.String {
throwJSException(b.runtime, "first argument must be the message to sign")
}
if !account.IsString() {
throwJSException("second argument must be the account to sign with")
if account.ExportType().Kind() != reflect.String {
throwJSException(b.runtime, "second argument must be the account to sign with")
}
// if the password is not given or null ask the user and ensure password is a string
if passwd.IsUndefined() || passwd.IsNull() {
if goja.IsUndefined(passwd) || goja.IsNull(passwd) {
fmt.Fprintf(b.printer, "Give password for account %s\n", account)
if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
} else {
passwd, _ = otto.ToValue(input)
passwd = b.runtime.ToValue(input)
}
}
if !passwd.IsString() {
throwJSException("third argument must be the password to unlock the account")
if passwd.ExportType().Kind() != reflect.String {
throwJSException(b.runtime, "third argument must be the password to unlock the account")
}
// Send the request to the backend and return
val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd)
sign, callable := goja.AssertFunction(b.runtime.Get("jeth.unlockAccount"))
if !callable {
throwJSException(b.runtime, "jeth.unlockAccount is not callable")
}
val, err := sign(goja.Null(), message, account, passwd)
if err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
return val
}
// Sleep will block the console for the specified number of seconds.
func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) {
if call.Argument(0).IsNumber() {
sleep, _ := call.Argument(0).ToInteger()
func (b *bridge) Sleep(call goja.FunctionCall) (response goja.Value) {
if IsNumber(call.Argument(0)) {
sleep := call.Argument(0).ToInteger()
time.Sleep(time.Duration(sleep) * time.Second)
return otto.TrueValue()
return b.runtime.ToValue(true)
}
return throwJSException("usage: sleep(<number of seconds>)")
return throwJSException(b.runtime, "usage: sleep(<number of seconds>)")
}
// SleepBlocks will block the console for a specified number of new blocks optionally
// until the given timeout is reached.
func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) SleepBlocks(call goja.FunctionCall) (response goja.Value) {
var (
blocks = int64(0)
sleep = int64(9999999999999999) // indefinitely
)
// Parse the input parameters for the sleep
nArgs := len(call.ArgumentList)
nArgs := len(call.Arguments)
if nArgs == 0 {
throwJSException("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
throwJSException(b.runtime, "usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
}
if nArgs >= 1 {
if call.Argument(0).IsNumber() {
blocks, _ = call.Argument(0).ToInteger()
if IsNumber(call.Argument(0)) {
blocks = call.Argument(0).ToInteger()
} else {
throwJSException("expected number as first argument")
throwJSException(b.runtime, "expected number as first argument")
}
}
if nArgs >= 2 {
if call.Argument(1).IsNumber() {
sleep, _ = call.Argument(1).ToInteger()
if IsNumber(call.Argument(1)) {
sleep = call.Argument(1).ToInteger()
} else {
throwJSException("expected number as second argument")
throwJSException(b.runtime, "expected number as second argument")
}
}
// go through the console, this will allow web3 to call the appropriate
// callbacks if a delayed response or notification is received.
blockNumber := func() int64 {
result, err := call.Otto.Run("eth.blockNumber")
if err != nil {
throwJSException(err.Error())
blockNumber, isFunc := goja.AssertFunction(b.runtime.Get("eth.blockNumber"))
if !isFunc {
throwJSException(b.runtime, "eth.blockNumber isn't a function")
}
block, err := result.ToInteger()
block, err := blockNumber(goja.Null())
if err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
return block
// XXX This will return 0 if blockNumber isn't an Integer. This is
// actually consistent with the current behavior (block number is 0
// until the sync is done) but not safe enough.
return block.ToInteger()
}
// Poll the current block number until either it ot a timeout is reached
targetBlockNr := blockNumber() + blocks
@ -343,11 +381,11 @@ func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
for time.Now().Before(deadline) {
if blockNumber() >= targetBlockNr {
return otto.TrueValue()
return b.runtime.ToValue(true)
}
time.Sleep(time.Second)
}
return otto.FalseValue()
return b.runtime.ToValue(false)
}
type jsonrpcCall struct {
@ -357,12 +395,15 @@ type jsonrpcCall struct {
}
// Send implements the web3 provider "send" method.
func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) Send(call goja.FunctionCall) (response goja.Value) {
// Remarshal the request into a Go value.
JSON, _ := call.Otto.Object("JSON")
reqVal, err := JSON.Call("stringify", call.Argument(0))
stringify, isFunc := goja.AssertFunction(b.runtime.Get("JSON.stringify"))
if !isFunc {
throwJSException(b.runtime, "JSON.stringify isn't a function")
}
reqVal, err := stringify(call.Argument(0))
if err != nil {
throwJSException(err.Error())
throwJSException(b.runtime, err.Error())
}
var (
rawReq = reqVal.String()
@ -381,9 +422,10 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
}
// Execute the requests.
resps, _ := call.Otto.Object("new Array()")
var resps []*goja.Object
for _, req := range reqs {
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
v, _ := b.runtime.RunString(`({"jsonrpc":"2.0"})`)
resp := v.ToObject(b.runtime)
resp.Set("id", req.ID)
var result json.RawMessage
err = b.client.Call(&result, req.Method, req.Params...)
@ -392,9 +434,13 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
if result == nil {
// Special case null because it is decoded as an empty
// raw message for some reason.
resp.Set("result", otto.NullValue())
resp.Set("result", goja.Null())
} else {
resultVal, err := JSON.Call("parse", string(result))
parse, isFunc := goja.AssertFunction(b.runtime.Get("JSON.parse"))
if !isFunc {
throwJSException(b.runtime, "JSON.parse isn't a function")
}
resultVal, err := parse(b.runtime.ToValue(string(result)))
if err != nil {
setError(resp, -32603, err.Error())
} else {
@ -406,33 +452,30 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
default:
setError(resp, -32603, err.Error())
}
resps.Call("push", resp)
resps = append(resps, resp)
}
// Return the responses either to the callback (if supplied)
// or directly as the return value.
if batch {
response = resps.Value()
response = b.runtime.ToValue(resps)
} else {
response, _ = resps.Get("0")
response = resps[0]
}
if fn := call.Argument(1); fn.Class() == "Function" {
fn.Call(otto.NullValue(), otto.NullValue(), response)
return otto.UndefinedValue()
if fn, isFunc := goja.AssertFunction(call.Argument(1)); isFunc {
fn(goja.Null(), goja.Null(), response)
return goja.Undefined()
}
return response
}
func setError(resp *otto.Object, code int, msg string) {
func setError(resp *goja.Object, code int, msg string) {
resp.Set("error", map[string]interface{}{"code": code, "message": msg})
}
// throwJSException panics on an otto.Value. The Otto VM will recover from the
// throwJSException panics on an goja.Value. The goja VM will recover from the
// Go panic and throw msg as a JavaScript error.
func throwJSException(msg interface{}) otto.Value {
val, err := otto.ToValue(msg)
if err != nil {
log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err)
}
func throwJSException(runtime *goja.Runtime, msg interface{}) goja.Value {
val := runtime.ToValue(msg)
panic(val)
}

View file

@ -28,12 +28,12 @@ import (
"strings"
"syscall"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/internal/web3ext"
"github.com/ethereum/go-ethereum/rpc"
"github.com/mattn/go-colorable"
"github.com/peterh/liner"
"github.com/robertkrimen/otto"
)
var (
@ -108,22 +108,24 @@ func New(config Config) (*Console, error) {
// the console's JavaScript namespaces based on the exposed modules.
func (c *Console) init(preload []string) error {
// Initialize the JavaScript <-> Go RPC bridge
bridge := newBridge(c.client, c.prompter, c.printer)
runtime := goja.New()
bridge := newBridge(c.client, c.prompter, c.printer, runtime)
c.jsre.Set("jeth", struct{}{})
c.jsre.Set("console", struct{}{})
jethObj, _ := c.jsre.Get("jeth")
jethObj.Object().Set("send", bridge.Send)
jethObj.Object().Set("sendAsync", bridge.Send)
jethObj := c.jsre.Get("jeth").ToObject(runtime)
jethObj.Set("send", bridge.Send)
jethObj.Set("sendAsync", bridge.Send)
consoleObj, _ := c.jsre.Get("console")
consoleObj.Object().Set("log", c.consoleOutput)
consoleObj.Object().Set("error", c.consoleOutput)
consoleObj := c.jsre.Get("console").ToObject(runtime)
consoleObj.Set("log", c.consoleOutput)
consoleObj.Set("error", c.consoleOutput)
// Load all the internal utility JavaScript libraries
if err := c.jsre.Compile("bignumber.js", jsre.BignumberJs); err != nil {
if err := c.jsre.Compile("bignumber.js", string(jsre.BignumberJs)); err != nil {
return fmt.Errorf("bignumber.js: %v", err)
}
if err := c.jsre.Compile("web3.js", jsre.Web3Js); err != nil {
if err := c.jsre.Compile("web3.js", string(jsre.Web3Js)); err != nil {
return fmt.Errorf("web3.js: %v", err)
}
if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil {
@ -148,7 +150,7 @@ func (c *Console) init(preload []string) error {
return fmt.Errorf("%s.js: %v", api, err)
}
flatten += fmt.Sprintf("var %s = web3.%s; ", api, api)
} else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() {
} else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.ToObject(runtime) != nil {
// Enable web3.js built-in extension if available.
flatten += fmt.Sprintf("var %s = web3.%s; ", api, api)
}
@ -162,16 +164,16 @@ func (c *Console) init(preload []string) error {
// If the console is in interactive mode, instrument password related methods to query the user
if c.prompter != nil {
// Retrieve the account management object to instrument
personal, err := c.jsre.Get("personal")
if err != nil {
return err
personal := c.jsre.Get("personal")
if personal == nil {
return fmt.Errorf("Could not find personal")
}
// Override the openWallet, unlockAccount, newAccount and sign methods since
// these require user interaction. Assign these method in the Console the
// original web3 callbacks. These will be called by the jeth.* methods after
// they got the password from the user and send the original web3 request to
// the backend.
if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface
if obj := personal.ToObject(runtime); obj != nil { // make sure the personal api is enabled over the interface
if _, err = c.jsre.Run(`jeth.openWallet = personal.openWallet;`); err != nil {
return fmt.Errorf("personal.openWallet: %v", err)
}
@ -191,11 +193,11 @@ func (c *Console) init(preload []string) error {
}
}
// The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
admin, err := c.jsre.Get("admin")
if err != nil {
return err
admin := c.jsre.Get("admin")
if admin == nil {
return fmt.Errorf("Could not find admin")
}
if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface
if obj := admin.ToObject(runtime); obj != nil { // make sure the admin api is enabled over the interface
obj.Set("sleepBlocks", bridge.SleepBlocks)
obj.Set("sleep", bridge.Sleep)
obj.Set("clearHistory", c.clearHistory)
@ -204,8 +206,8 @@ func (c *Console) init(preload []string) error {
for _, path := range preload {
if err := c.jsre.Exec(path); err != nil {
failure := err.Error()
if ottoErr, ok := err.(*otto.Error); ok {
failure = ottoErr.String()
if gojaErr, ok := err.(*goja.Exception); ok {
failure = gojaErr.String()
}
return fmt.Errorf("%s: %v", path, failure)
}
@ -235,13 +237,13 @@ func (c *Console) clearHistory() {
// consoleOutput is an override for the console.log and console.error methods to
// stream the output into the configured output stream instead of stdout.
func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value {
func (c *Console) consoleOutput(call goja.FunctionCall) goja.Value {
var output []string
for _, argument := range call.ArgumentList {
for _, argument := range call.Arguments {
output = append(output, fmt.Sprintf("%v", argument))
}
fmt.Fprintln(c.printer, strings.Join(output, " "))
return otto.Value{}
return goja.Null()
}
// AutoCompleteInput is a pre-assembled word completer to be used by the user

View file

@ -20,29 +20,32 @@ import (
"sort"
"strings"
"github.com/robertkrimen/otto"
"github.com/dop251/goja"
)
// CompleteKeywords returns potential continuations for the given line. Since line is
// evaluated, callers need to make sure that evaluating line does not have side effects.
func (jsre *JSRE) CompleteKeywords(line string) []string {
var results []string
jsre.Do(func(vm *otto.Otto) {
jsre.Do(func(vm *goja.Runtime) {
results = getCompletions(vm, line)
})
return results
}
func getCompletions(vm *otto.Otto, line string) (results []string) {
func getCompletions(vm *goja.Runtime, line string) (results []string) {
parts := strings.Split(line, ".")
objRef := "this"
prefix := line
var obj *goja.Object
if len(parts) > 1 {
objRef = strings.Join(parts[0:len(parts)-1], ".")
prefix = parts[len(parts)-1]
obj, _ = vm.Get(objRef).(*goja.Object)
} else {
obj = vm.GlobalObject()
}
obj, _ := vm.Object(objRef)
if obj == nil {
return nil
}
@ -59,9 +62,11 @@ func getCompletions(vm *otto.Otto, line string) (results []string) {
// Append opening parenthesis (for functions) or dot (for objects)
// if the line itself is the only completion.
if len(results) == 1 && results[0] == line {
obj, _ := vm.Object(line)
/* XXX Get will return `nil` et j'avais suppose que ca lancerait
une exception donc je dois tout revoir */
obj := vm.Get(line)
if obj != nil {
if obj.Class() == "Function" {
if _, isfunc := goja.AssertFunction(obj); isfunc {
results[0] += "("
} else {
results[0] += "."

View file

@ -26,9 +26,9 @@ import (
"math/rand"
"time"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/jsre/deps"
"github.com/robertkrimen/otto"
)
var (
@ -37,7 +37,7 @@ var (
)
/*
JSRE is a generic JS runtime environment embedding the otto JS interpreter.
JSRE is a generic JS runtime environment embedding the goja JS interpreter.
It provides some helper functions to
- load code from files
- run code snippets
@ -50,6 +50,7 @@ type JSRE struct {
evalQueue chan *evalReq
stopEventLoop chan bool
closed chan struct{}
vm *goja.Runtime
}
// jsTimer is a single timer instance with a callback function
@ -57,12 +58,12 @@ type jsTimer struct {
timer *time.Timer
duration time.Duration
interval bool
call otto.FunctionCall
call goja.FunctionCall
}
// evalReq is a serialized vm execution request processed by runEventLoop.
type evalReq struct {
fn func(vm *otto.Otto)
fn func(vm *goja.Runtime)
done chan bool
}
@ -99,21 +100,21 @@ func randomSource() *rand.Rand {
// serialized way and calls timer callback functions at the appropriate time.
// Exported functions always access the vm through the event queue. You can
// call the functions of the otto vm directly to circumvent the queue. These
// call the functions of the goja vm directly to circumvent the queue. These
// functions should be used if and only if running a routine that was already
// called from JS through an RPC call.
func (re *JSRE) runEventLoop() {
defer close(re.closed)
vm := otto.New()
vm := goja.New()
r := randomSource()
vm.SetRandomSource(r.Float64)
vm.SetRandSource(r.Float64)
registry := map[*jsTimer]*jsTimer{}
ready := make(chan *jsTimer)
newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) {
delay, _ := call.Argument(1).ToInteger()
newTimer := func(call goja.FunctionCall, interval bool) (*jsTimer, goja.Value) {
delay := call.Argument(1).ToInteger()
if 0 >= delay {
delay = 1
}
@ -128,40 +129,36 @@ func (re *JSRE) runEventLoop() {
ready <- timer
})
value, err := call.Otto.ToValue(timer)
if err != nil {
panic(err)
}
return timer, value
return timer, re.vm.ToValue(timer)
}
setTimeout := func(call otto.FunctionCall) otto.Value {
setTimeout := func(call goja.FunctionCall) goja.Value {
_, value := newTimer(call, false)
return value
}
setInterval := func(call otto.FunctionCall) otto.Value {
setInterval := func(call goja.FunctionCall) goja.Value {
_, value := newTimer(call, true)
return value
}
clearTimeout := func(call otto.FunctionCall) otto.Value {
timer, _ := call.Argument(0).Export()
clearTimeout := func(call goja.FunctionCall) goja.Value {
timer := call.Argument(0).Export()
if timer, ok := timer.(*jsTimer); ok {
timer.timer.Stop()
delete(registry, timer)
}
return otto.UndefinedValue()
return goja.Undefined()
}
vm.Set("_setTimeout", setTimeout)
vm.Set("_setInterval", setInterval)
vm.Run(`var setTimeout = function(args) {
vm.RunString(`var setTimeout = function(args) {
if (arguments.length < 1) {
throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
}
return _setTimeout.apply(this, arguments);
}`)
vm.Run(`var setInterval = function(args) {
vm.RunString(`var setInterval = function(args) {
if (arguments.length < 1) {
throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
}
@ -178,8 +175,8 @@ loop:
case timer := <-ready:
// execute callback, remove/reschedule the timer
var arguments []interface{}
if len(timer.call.ArgumentList) > 2 {
tmp := timer.call.ArgumentList[2:]
if len(timer.call.Arguments) > 2 {
tmp := timer.call.Arguments[2:]
arguments = make([]interface{}, 2+len(tmp))
for i, value := range tmp {
arguments[i+2] = value
@ -187,11 +184,12 @@ loop:
} else {
arguments = make([]interface{}, 1)
}
arguments[0] = timer.call.ArgumentList[0]
_, err := vm.Call(`Function.call.call`, nil, arguments...)
if err != nil {
fmt.Println("js error:", err, arguments)
arguments[0] = timer.call.Arguments[0]
call, isFunc := goja.AssertFunction(vm.Get(`Function.call.call`))
if !isFunc {
panic(vm.ToValue("js error: Function.call.call is not a function"))
}
call(goja.Null(), timer.call.Arguments...)
_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
if timer.interval && inreg {
@ -223,7 +221,7 @@ loop:
}
// Do executes the given function on the JS event loop.
func (re *JSRE) Do(fn func(*otto.Otto)) {
func (re *JSRE) Do(fn func(*goja.Runtime)) {
done := make(chan bool)
req := &evalReq{fn, done}
re.evalQueue <- req
@ -246,13 +244,13 @@ func (re *JSRE) Exec(file string) error {
if err != nil {
return err
}
var script *otto.Script
re.Do(func(vm *otto.Otto) {
script, err = vm.Compile(file, code)
var script *goja.Program
re.Do(func(vm *goja.Runtime) {
script, err = goja.Compile(file, string(code), false)
if err != nil {
return
}
_, err = vm.Run(script)
_, err = vm.RunProgram(script)
})
return err
}
@ -264,43 +262,38 @@ func (re *JSRE) Bind(name string, v interface{}) error {
}
// Run runs a piece of JS code.
func (re *JSRE) Run(code string) (v otto.Value, err error) {
re.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
func (re *JSRE) Run(code string) (v goja.Value, err error) {
re.Do(func(vm *goja.Runtime) { v, err = vm.RunString(code) })
return v, err
}
// Get returns the value of a variable in the JS environment.
func (re *JSRE) Get(ns string) (v otto.Value, err error) {
re.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
return v, err
func (re *JSRE) Get(ns string) (v goja.Value) {
re.Do(func(vm *goja.Runtime) { v = vm.Get(ns) })
return v
}
// Set assigns value v to a variable in the JS environment.
func (re *JSRE) Set(ns string, v interface{}) (err error) {
re.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) })
return err
}
// loadScript executes a JS script from inside the currently executing JS code.
func (re *JSRE) loadScript(call otto.FunctionCall) otto.Value {
file, err := call.Argument(0).ToString()
if err != nil {
// TODO: throw exception
return otto.FalseValue()
}
func (re *JSRE) loadScript(call goja.FunctionCall) goja.Value {
file := call.Argument(0).ToString().String()
file = common.AbsolutePath(re.assetPath, file)
source, err := ioutil.ReadFile(file)
if err != nil {
// TODO: throw exception
return otto.FalseValue()
// Panicking with a goja.Value arg will cause a JS exception
// in the caller.
panic(re.vm.ToValue(fmt.Sprintf("Could not read file %s: %v", file, err)))
}
if _, err := compileAndRun(call.Otto, file, source); err != nil {
// TODO: throw exception
fmt.Println("err:", err)
return otto.FalseValue()
value, err := compileAndRun(re.vm, file, string(source))
if err != nil {
panic(re.vm.ToValue(fmt.Sprintf("Error while compiling or running script: %v", err)))
}
// TODO: return evaluation result
return otto.TrueValue()
return value
}
// Evaluate executes code and pretty prints the result to the specified output
@ -308,8 +301,8 @@ func (re *JSRE) loadScript(call otto.FunctionCall) otto.Value {
func (re *JSRE) Evaluate(code string, w io.Writer) error {
var fail error
re.Do(func(vm *otto.Otto) {
val, err := vm.Run(code)
re.Do(func(vm *goja.Runtime) {
val, err := vm.RunString(code)
if err != nil {
prettyError(vm, err, w)
} else {
@ -321,15 +314,15 @@ func (re *JSRE) Evaluate(code string, w io.Writer) error {
}
// Compile compiles and then runs a piece of JS code.
func (re *JSRE) Compile(filename string, src interface{}) (err error) {
re.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
func (re *JSRE) Compile(filename string, src string) (err error) {
re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) })
return err
}
func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
script, err := vm.Compile(filename, src)
func compileAndRun(vm *goja.Runtime, filename string, src string) (goja.Value, error) {
script, err := goja.Compile(filename, src, true)
if err != nil {
return otto.Value{}, err
return goja.Null(), err
}
return vm.Run(script)
return vm.RunProgram(script)
}

View file

@ -19,12 +19,13 @@ package jsre
import (
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"github.com/dop251/goja"
"github.com/fatih/color"
"github.com/robertkrimen/otto"
)
const (
@ -52,29 +53,29 @@ var boringKeys = map[string]bool{
}
// prettyPrint writes value to standard output.
func prettyPrint(vm *otto.Otto, value otto.Value, w io.Writer) {
func prettyPrint(vm *goja.Runtime, value goja.Value, w io.Writer) {
ppctx{vm: vm, w: w}.printValue(value, 0, false)
}
// prettyError writes err to standard output.
func prettyError(vm *otto.Otto, err error, w io.Writer) {
func prettyError(vm *goja.Runtime, err error, w io.Writer) {
failure := err.Error()
if ottoErr, ok := err.(*otto.Error); ok {
failure = ottoErr.String()
if gojaErr, ok := err.(*goja.Exception); ok {
failure = gojaErr.String()
}
fmt.Fprint(w, ErrorColor("%s", failure))
}
func (re *JSRE) prettyPrintJS(call otto.FunctionCall) otto.Value {
for _, v := range call.ArgumentList {
prettyPrint(call.Otto, v, re.output)
func (re *JSRE) prettyPrintJS(call goja.FunctionCall) goja.Value {
for _, v := range call.Arguments {
prettyPrint(re.vm, v, re.output)
fmt.Fprintln(re.output)
}
return otto.UndefinedValue()
return goja.Undefined()
}
type ppctx struct {
vm *otto.Otto
vm *goja.Runtime
w io.Writer
}
@ -82,35 +83,40 @@ func (ctx ppctx) indent(level int) string {
return strings.Repeat(indentString, level)
}
func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) {
func (ctx ppctx) printValue(v goja.Value, level int, inArray bool) {
switch {
case v.IsObject():
ctx.printObject(v.Object(), level, inArray)
case v.IsNull():
case goja.IsNull(v):
fmt.Fprint(ctx.w, SpecialColor("null"))
case v.IsUndefined():
case goja.IsUndefined(v):
fmt.Fprint(ctx.w, SpecialColor("undefined"))
case v.IsString():
s, _ := v.ToString()
fmt.Fprint(ctx.w, StringColor("%q", s))
case v.IsBoolean():
b, _ := v.ToBoolean()
fmt.Fprint(ctx.w, SpecialColor("%t", b))
case v.IsNaN():
case goja.IsNaN(v):
fmt.Fprint(ctx.w, NumberColor("NaN"))
case v.IsNumber():
s, _ := v.ToString()
fmt.Fprint(ctx.w, NumberColor("%s", s))
default:
fmt.Fprint(ctx.w, "<unprintable>")
switch v.ExportType().Kind() {
case reflect.String:
s := v.ToString().String()
fmt.Fprint(ctx.w, StringColor("%q", s))
case reflect.Bool:
b := v.ToBoolean()
fmt.Fprint(ctx.w, SpecialColor("%t", b))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s := v.ToString().String()
fmt.Fprint(ctx.w, NumberColor("%s", s))
default:
if obj, ok := v.(*goja.Object); ok {
ctx.printObject(obj, level, inArray)
} else {
fmt.Fprint(ctx.w, "<unprintable>")
}
}
}
}
func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
switch obj.Class() {
func (ctx ppctx) printObject(obj *goja.Object, level int, inArray bool) {
switch obj.ClassName() {
case "Array", "GoArray":
lv, _ := obj.Get("length")
len, _ := lv.ToInteger()
lv := obj.Get("length")
len := lv.ToInteger()
if len == 0 {
fmt.Fprintf(ctx.w, "[]")
return
@ -121,8 +127,8 @@ func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
}
fmt.Fprint(ctx.w, "[")
for i := int64(0); i < len; i++ {
el, err := obj.Get(strconv.FormatInt(i, 10))
if err == nil {
el := obj.Get(strconv.FormatInt(i, 10))
if el != nil {
ctx.printValue(el, level+1, true)
}
if i < len-1 {
@ -149,7 +155,7 @@ func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
}
fmt.Fprintln(ctx.w, "{")
for i, k := range keys {
v, _ := obj.Get(k)
v := obj.Get(k)
fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k)
ctx.printValue(v, level+1, false)
if i < len(keys)-1 {
@ -163,29 +169,25 @@ func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
fmt.Fprintf(ctx.w, "%s}", ctx.indent(level))
case "Function":
// Use toString() to display the argument list if possible.
if robj, err := obj.Call("toString"); err != nil {
fmt.Fprint(ctx.w, FunctionColor("function()"))
} else {
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
desc = strings.Replace(desc, " (", "(", 1)
fmt.Fprint(ctx.w, FunctionColor("%s", desc))
}
robj := obj.ToString()
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
desc = strings.Replace(desc, " (", "(", 1)
fmt.Fprint(ctx.w, FunctionColor("%s", desc))
case "RegExp":
fmt.Fprint(ctx.w, StringColor("%s", toString(obj)))
default:
if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel {
s, _ := obj.Call("toString")
fmt.Fprintf(ctx.w, "<%s %s>", obj.Class(), s.String())
if level <= maxPrettyPrintLevel {
s := obj.ToString().String()
fmt.Fprintf(ctx.w, "<%s %s>", obj.ClassName(), s)
} else {
fmt.Fprintf(ctx.w, "<%s>", obj.Class())
fmt.Fprintf(ctx.w, "<%s>", obj.ClassName())
}
}
}
func (ctx ppctx) fields(obj *otto.Object) []string {
func (ctx ppctx) fields(obj *goja.Object) []string {
var (
vals, methods []string
seen = make(map[string]bool)
@ -195,7 +197,8 @@ func (ctx ppctx) fields(obj *otto.Object) []string {
return
}
seen[k] = true
if v, _ := obj.Get(k); v.IsFunction() {
if _, callable := goja.AssertFunction(obj.Get(k)); callable {
methods = append(methods, k)
} else {
vals = append(vals, k)
@ -207,13 +210,13 @@ func (ctx ppctx) fields(obj *otto.Object) []string {
return append(vals, methods...)
}
func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string)) {
func iterOwnAndConstructorKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
seen := make(map[string]bool)
iterOwnKeys(vm, obj, func(prop string) {
seen[prop] = true
f(prop)
})
if cp := constructorPrototype(obj); cp != nil {
if cp := constructorPrototype(vm, obj); cp != nil {
iterOwnKeys(vm, cp, func(prop string) {
if !seen[prop] {
f(prop)
@ -222,10 +225,10 @@ func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string))
}
}
func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) {
Object, _ := vm.Object("Object")
rv, _ := Object.Call("getOwnPropertyNames", obj.Value())
gv, _ := rv.Export()
func iterOwnKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
getOwnPropertyNames, _ := goja.AssertFunction(vm.Get("Object.getOwnPropertyNames"))
rv, _ := getOwnPropertyNames(obj)
gv := rv.Export()
switch gv := gv.(type) {
case []interface{}:
for _, v := range gv {
@ -240,32 +243,34 @@ func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) {
}
}
func (ctx ppctx) isBigNumber(v *otto.Object) bool {
func (ctx ppctx) isBigNumber(v *goja.Object) bool {
// Handle numbers with custom constructor.
if v, _ := v.Get("constructor"); v.Object() != nil {
if strings.HasPrefix(toString(v.Object()), "function BigNumber") {
if obj := v.Get("constructor").ToObject(ctx.vm); obj != nil {
if strings.HasPrefix(toString(obj), "function BigNumber") {
return true
}
}
// Handle default constructor.
BigNumber, _ := ctx.vm.Object("BigNumber.prototype")
BigNumber := ctx.vm.Get("BigNumber.prototype").ToObject(ctx.vm)
if BigNumber == nil {
return false
}
bv, _ := BigNumber.Call("isPrototypeOf", v)
b, _ := bv.ToBoolean()
return b
isPrototypeOf, exists := goja.AssertFunction(BigNumber.Get("isPrototypeOf"))
if !exists {
return false
}
bv, _ := isPrototypeOf(v)
return bv.ToBoolean()
}
func toString(obj *otto.Object) string {
s, _ := obj.Call("toString")
return s.String()
func toString(obj *goja.Object) string {
return obj.ToString().String()
}
func constructorPrototype(obj *otto.Object) *otto.Object {
if v, _ := obj.Get("constructor"); v.Object() != nil {
if v, _ = v.Object().Get("prototype"); v.Object() != nil {
return v.Object()
func constructorPrototype(vm *goja.Runtime, obj *goja.Object) *goja.Object {
if v := obj.Get("constructor"); v != nil {
if v := v.ToObject(vm).Get("prototype"); v != nil {
return v.ToObject(vm)
}
}
return nil

15
vendor/github.com/dop251/goja/LICENSE generated vendored Normal file
View file

@ -0,0 +1,15 @@
Copyright (c) 2016 Dmitry Panov
Copyright (c) 2012 Robert Krimen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

253
vendor/github.com/dop251/goja/README.md generated vendored Normal file
View file

@ -0,0 +1,253 @@
goja
====
ECMAScript 5.1(+) implementation in Go.
[![GoDoc](https://godoc.org/github.com/dop251/goja?status.svg)](https://godoc.org/github.com/dop251/goja)
Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and
performance.
This project was largely inspired by [otto](https://github.com/robertkrimen/otto).
Features
--------
* Full ECMAScript 5.1 support (yes, including regex and strict mode).
* Passes nearly all [tc39 tests](https://github.com/tc39/test262) tagged with es5id. The goal is to pass all of them. Note, the last working commit is https://github.com/tc39/test262/commit/1ba3a7c4a93fc93b3d0d7e4146f59934a896837d. The next commit made use of template strings which goja does not support.
* Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
* Sourcemaps.
FAQ
---
### How fast is it?
Although it's faster than many scripting language implementations in Go I have seen
(for example it's 6-7 times faster than otto on average) it is not a
replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine.
You can find some benchmarks [here](https://github.com/dop251/goja/issues/2).
### Why would I want to use it over a V8 wrapper?
It greatly depends on your usage scenario. If most of the work is done in javascript
(for example crypto or any other heavy calculations) you are definitely better off with V8.
If you need a scripting language that drives an engine written in Go so
you need to make frequent calls between Go and javascript passing complex data structures
then the cgo overhead may outweigh the benefits of having a faster javascript engine.
Because it's written in pure Go there are no external dependencies, it's very easy to build and it
should run on any platform supported by Go.
It gives you a much better control over execution environment so can be useful for research.
### Is it goroutine-safe?
No. An instance of goja.Runtime can only be used by a single goroutine
at a time. You can create as many instances of Runtime as you like but
it's not possible to pass object values between runtimes.
### Where is setTimeout()?
setTimeout() assumes concurrent execution of code which requires an execution
environment, for example an event loop similar to nodejs or a browser.
There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some of the NodeJS functionality
and it includes an event loop.
### Can you implement (feature X from ES6 or higher)?
It's very unlikely that I will be adding new functionality any time soon. It don't have enough time
for adding full ES6 support and I don't want to end up with something that is stuck in between ES5 and ES6.
Most of the new features are available through shims and transpilers. Goja can run Babel and any
other transpiler as long as it's written in ES5. You can even add a wrapper that will do the translation
on the fly. Sourcemaps are supported.
### How do I contribute?
Before submitting a pull request please make sure that:
- You followed ECMA standard as close as possible. If adding a new feature make sure you've read the specification,
do not just base it on a couple of examples that work fine.
- Your change does not have a significant negative impact on performance (unless it's a bugfix and it's unavoidable)
- It passes all relevant tc39 tests.
Current Status
--------------
* API is still work in progress and is subject to change.
* Some of the AnnexB functionality is missing.
* No typed arrays yet.
Basic Example
-------------
```go
vm := goja.New()
v, err := vm.RunString("2 + 2")
if err != nil {
panic(err)
}
if num := v.Export().(int64); num != 4 {
panic(num)
}
```
Passing Values to JS
--------------------
Any Go value can be passed to JS using Runtime.ToValue() method. Primitive types (ints and uints, floats, string, bool)
are converted to the corresponding JavaScript primitives.
*func(FunctionCall) Value* is treated as a native JavaScript function.
*func(ConstructorCall) \*Object* is treated as a JavaScript constructor (see Native Constructors).
*map[string]interface{}* is converted into a host object that largely behaves like a JavaScript Object.
*[]interface{}* is converted into a host object that behaves largely like a JavaScript Array, however it's not extensible
because extending it can change the pointer so it becomes detached from the original.
**[]interface{}* is same as above, but the array becomes extensible.
A function is wrapped within a native JavaScript function. When called the arguments are automatically converted to
the appropriate Go types. If conversion is not possible, a TypeError is thrown.
A slice type is converted into a generic reflect based host object that behaves similar to an unexpandable Array.
A map type with numeric or string keys and no methods is converted into a host object where properties are map keys.
A map type with methods is converted into a host object where properties are method names,
the map values are not accessible. This is to avoid ambiguity between m\["Property"\] and m.Property.
Any other type is converted to a generic reflect based host object. Depending on the underlying type it behaves similar
to a Number, String, Boolean or Object.
Note that these conversions wrap the original value which means any changes made inside JS
are reflected on the value and calling Export() returns the original value. This applies to all
reflect based types.
Exporting Values from JS
------------------------
A JS value can be exported into its default Go representation using Value.Export() method.
Alternatively it can be exported into a specific Go variable using Runtime.ExportTo() method.
Native Constructors
-------------------
In order to implement a constructor function in Go:
```go
func MyObject(call goja.ConstructorCall) *Object {
// call.This contains the newly created object as per http://www.ecma-international.org/ecma-262/5.1/index.html#sec-13.2.2
// call.Arguments contain arguments passed to the function
call.This.Set("method", method)
//...
// If return value is a non-nil *Object, it will be used instead of call.This
// This way it is possible to return a Go struct or a map converted
// into goja.Value using runtime.ToValue(), however in this case
// instanceof will not work as expected.
return nil
}
runtime.Set("MyObject", MyObject)
```
Then it can be used in JS as follows:
```js
var o = new MyObject(arg);
var o1 = MyObject(arg); // same thing
o instanceof MyObject && o1 instanceof MyObject; // true
```
Regular Expressions
-------------------
Goja uses the embedded Go regexp library where possible, otherwise it falls back to [regexp2](https://github.com/dlclark/regexp2).
Exceptions
----------
Any exception thrown in JavaScript is returned as an error of type *Exception. It is possible to extract the value thrown
by using the Value() method:
```go
vm := New()
_, err := vm.RunString(`
throw("Test");
`)
if jserr, ok := err.(*Exception); ok {
if jserr.Value().Export() != "Test" {
panic("wrong value")
}
} else {
panic("wrong type")
}
```
If a native Go function panics with a Value, it is thrown as a Javascript exception (and therefore can be caught):
```go
var vm *Runtime
func Test() {
panic(vm.ToValue("Error"))
}
vm = New()
vm.Set("Test", Test)
_, err := vm.RunString(`
try {
Test();
} catch(e) {
if (e !== "Error") {
throw e;
}
}
`)
if err != nil {
panic(err)
}
```
Interrupting
------------
```go
func TestInterrupt(t *testing.T) {
const SCRIPT = `
var i = 0;
for (;;) {
i++;
}
`
vm := New()
time.AfterFunc(200 * time.Millisecond, func() {
vm.Interrupt("halt")
})
_, err := vm.RunString(SCRIPT)
if err == nil {
t.Fatal("Err is nil")
}
// err is of type *InterruptError and its Value() method returns whatever has been passed to vm.Interrupt()
}
```
NodeJS Compatibility
--------------------
There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some of the NodeJS functionality.

486
vendor/github.com/dop251/goja/array.go generated vendored Normal file
View file

@ -0,0 +1,486 @@
package goja
import (
"math"
"reflect"
"strconv"
)
type arrayObject struct {
baseObject
values []Value
length int64
objCount int64
propValueCount int
lengthProp valueProperty
}
func (a *arrayObject) init() {
a.baseObject.init()
a.lengthProp.writable = true
a._put("length", &a.lengthProp)
}
func (a *arrayObject) getLength() Value {
return intToValue(a.length)
}
func (a *arrayObject) _setLengthInt(l int64, throw bool) bool {
if l >= 0 && l <= math.MaxUint32 {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
var s int64
if a.length < int64(len(a.values)) {
s = a.length - 1
} else {
s = int64(len(a.values)) - 1
}
for i := s; i >= l; i-- {
if prop, ok := a.values[i].(*valueProperty); ok {
if !prop.configurable {
l = i + 1
ret = false
break
}
a.propValueCount--
}
}
}
}
if l <= int64(len(a.values)) {
if l >= 16 && l < int64(cap(a.values))>>2 {
ar := make([]Value, l)
copy(ar, a.values)
a.values = ar
} else {
ar := a.values[l:len(a.values)]
for i, _ := range ar {
ar[i] = nil
}
a.values = a.values[:l]
}
}
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
}
func (a *arrayObject) setLengthInt(l int64, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(l, throw)
}
func (a *arrayObject) setLength(v Value, throw bool) bool {
l, ok := toIntIgnoreNegZero(v)
if ok && l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
if ok {
return a._setLengthInt(l, throw)
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
}
func (a *arrayObject) getIdx(idx int64, origNameStr string, origName Value) (v Value) {
if idx >= 0 && idx < int64(len(a.values)) {
v = a.values[idx]
}
if v == nil && a.prototype != nil {
if origName != nil {
v = a.prototype.self.getProp(origName)
} else {
v = a.prototype.self.getPropStr(origNameStr)
}
}
return
}
func (a *arrayObject) sortLen() int64 {
return int64(len(a.values))
}
func (a *arrayObject) sortGet(i int64) Value {
v := a.values[i]
if p, ok := v.(*valueProperty); ok {
v = p.get(a.val)
}
return v
}
func (a *arrayObject) swap(i, j int64) {
a.values[i], a.values[j] = a.values[j], a.values[i]
}
func toIdx(v Value) (idx int64) {
idx = -1
if idxVal, ok1 := v.(valueInt); ok1 {
idx = int64(idxVal)
} else {
if i, err := strconv.ParseInt(v.String(), 10, 64); err == nil {
idx = i
}
}
if idx >= 0 && idx < math.MaxUint32 {
return
}
return -1
}
func strToIdx(s string) (idx int64) {
idx = -1
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
idx = i
}
if idx >= 0 && idx < math.MaxUint32 {
return
}
return -1
}
func (a *arrayObject) getProp(n Value) Value {
if idx := toIdx(n); idx >= 0 {
return a.getIdx(idx, "", n)
}
if n.String() == "length" {
return a.getLengthProp()
}
return a.baseObject.getProp(n)
}
func (a *arrayObject) getLengthProp() Value {
a.lengthProp.value = intToValue(a.length)
return &a.lengthProp
}
func (a *arrayObject) getPropStr(name string) Value {
if i := strToIdx(name); i >= 0 {
return a.getIdx(i, name, nil)
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getPropStr(name)
}
func (a *arrayObject) getOwnProp(name string) Value {
if i := strToIdx(name); i >= 0 {
if i >= 0 && i < int64(len(a.values)) {
return a.values[i]
}
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getOwnProp(name)
}
func (a *arrayObject) putIdx(idx int64, val Value, throw bool, origNameStr string, origName Value) {
var prop Value
if idx < int64(len(a.values)) {
prop = a.values[idx]
}
if prop == nil {
if a.prototype != nil {
var pprop Value
if origName != nil {
pprop = a.prototype.self.getProp(origName)
} else {
pprop = a.prototype.self.getPropStr(origNameStr)
}
if pprop, ok := pprop.(*valueProperty); ok {
if !pprop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return
}
if pprop.accessor {
pprop.set(a.val, val)
return
}
}
}
if !a.extensible {
a.val.runtime.typeErrorResult(throw)
return
}
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return
}
}
if idx >= int64(len(a.values)) {
if !a.expand(idx) {
a.val.self.(*sparseArrayObject).putIdx(idx, val, throw, origNameStr, origName)
return
}
}
} else {
if prop, ok := prop.(*valueProperty); ok {
if !prop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return
}
prop.set(a.val, val)
return
}
}
a.values[idx] = val
a.objCount++
}
func (a *arrayObject) put(n Value, val Value, throw bool) {
if idx := toIdx(n); idx >= 0 {
a.putIdx(idx, val, throw, "", n)
} else {
if n.String() == "length" {
a.setLength(val, throw)
} else {
a.baseObject.put(n, val, throw)
}
}
}
func (a *arrayObject) putStr(name string, val Value, throw bool) {
if idx := strToIdx(name); idx >= 0 {
a.putIdx(idx, val, throw, name, nil)
} else {
if name == "length" {
a.setLength(val, throw)
} else {
a.baseObject.putStr(name, val, throw)
}
}
}
type arrayPropIter struct {
a *arrayObject
recursive bool
idx int
}
func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.values) {
name := strconv.Itoa(i.idx)
prop := i.a.values[i.idx]
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
return i.a.baseObject._enumerate(i.recursive)()
}
func (a *arrayObject) _enumerate(recursive bool) iterNextFunc {
return (&arrayPropIter{
a: a,
recursive: recursive,
}).next
}
func (a *arrayObject) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: a._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (a *arrayObject) hasOwnProperty(n Value) bool {
if idx := toIdx(n); idx >= 0 {
return idx < int64(len(a.values)) && a.values[idx] != nil && a.values[idx] != _undefined
} else {
return a.baseObject.hasOwnProperty(n)
}
}
func (a *arrayObject) hasOwnPropertyStr(name string) bool {
if idx := strToIdx(name); idx >= 0 {
return idx < int64(len(a.values)) && a.values[idx] != nil && a.values[idx] != _undefined
} else {
return a.baseObject.hasOwnPropertyStr(name)
}
}
func (a *arrayObject) expand(idx int64) bool {
targetLen := idx + 1
if targetLen > int64(len(a.values)) {
if targetLen < int64(cap(a.values)) {
a.values = a.values[:targetLen]
} else {
if idx > 4096 && (a.objCount == 0 || idx/a.objCount > 10) {
//log.Println("Switching standard->sparse")
sa := &sparseArrayObject{
baseObject: a.baseObject,
length: a.length,
propValueCount: a.propValueCount,
}
sa.setValues(a.values)
sa.val.self = sa
sa.init()
sa.lengthProp.writable = a.lengthProp.writable
return false
} else {
// Use the same algorithm as in runtime.growSlice
newcap := int64(cap(a.values))
doublecap := newcap + newcap
if targetLen > doublecap {
newcap = targetLen
} else {
if len(a.values) < 1024 {
newcap = doublecap
} else {
for newcap < targetLen {
newcap += newcap / 4
}
}
}
newValues := make([]Value, targetLen, newcap)
copy(newValues, a.values)
a.values = newValues
}
}
}
return true
}
func (r *Runtime) defineArrayLength(prop *valueProperty, descr propertyDescr, setter func(Value, bool) bool, throw bool) bool {
ret := true
if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
ret = false
goto Reject
}
if newLen := descr.Value; newLen != nil {
ret = setter(newLen, false)
} else {
ret = true
}
if descr.Writable != FLAG_NOT_SET {
w := descr.Writable.Bool()
if prop.writable {
prop.writable = w
} else {
if w {
ret = false
goto Reject
}
}
}
Reject:
if !ret {
r.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
func (a *arrayObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
if idx := toIdx(n); idx >= 0 {
var existing Value
if idx < int64(len(a.values)) {
existing = a.values[idx]
}
prop, ok := a.baseObject._defineOwnProperty(n, existing, descr, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if a.expand(idx) {
a.values[idx] = prop
a.objCount++
if _, ok := prop.(*valueProperty); ok {
a.propValueCount++
}
} else {
a.val.self.(*sparseArrayObject).putIdx(idx, prop, throw, "", nil)
}
}
return ok
} else {
if n.String() == "length" {
return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
}
return a.baseObject.defineOwnProperty(n, descr, throw)
}
}
func (a *arrayObject) _deleteProp(idx int64, throw bool) bool {
if idx < int64(len(a.values)) {
if v := a.values[idx]; v != nil {
if p, ok := v.(*valueProperty); ok {
if !p.configurable {
a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.ToString())
return false
}
a.propValueCount--
}
a.values[idx] = nil
a.objCount--
}
}
return true
}
func (a *arrayObject) delete(n Value, throw bool) bool {
if idx := toIdx(n); idx >= 0 {
return a._deleteProp(idx, throw)
}
return a.baseObject.delete(n, throw)
}
func (a *arrayObject) deleteStr(name string, throw bool) bool {
if idx := strToIdx(name); idx >= 0 {
return a._deleteProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
}
func (a *arrayObject) export() interface{} {
arr := make([]interface{}, a.length)
for i, v := range a.values {
if v != nil {
arr[i] = v.Export()
}
}
return arr
}
func (a *arrayObject) exportType() reflect.Type {
return reflectTypeArray
}
func (a *arrayObject) setValuesFromSparse(items []sparseArrayItem) {
a.values = make([]Value, int(items[len(items)-1].idx+1))
for _, item := range items {
a.values[item.idx] = item.value
}
a.objCount = int64(len(items))
}

455
vendor/github.com/dop251/goja/array_sparse.go generated vendored Normal file
View file

@ -0,0 +1,455 @@
package goja
import (
"math"
"reflect"
"sort"
"strconv"
)
type sparseArrayItem struct {
idx int64
value Value
}
type sparseArrayObject struct {
baseObject
items []sparseArrayItem
length int64
propValueCount int
lengthProp valueProperty
}
func (a *sparseArrayObject) init() {
a.baseObject.init()
a.lengthProp.writable = true
a._put("length", &a.lengthProp)
}
func (a *sparseArrayObject) getLength() Value {
return intToValue(a.length)
}
func (a *sparseArrayObject) findIdx(idx int64) int {
return sort.Search(len(a.items), func(i int) bool {
return a.items[i].idx >= idx
})
}
func (a *sparseArrayObject) _setLengthInt(l int64, throw bool) bool {
if l >= 0 && l <= math.MaxUint32 {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.items) - 1; i >= 0; i-- {
item := a.items[i]
if item.idx <= l {
break
}
if prop, ok := item.value.(*valueProperty); ok {
if !prop.configurable {
l = item.idx + 1
ret = false
break
}
a.propValueCount--
}
}
}
}
idx := a.findIdx(l)
aa := a.items[idx:]
for i, _ := range aa {
aa[i].value = nil
}
a.items = a.items[:idx]
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
}
func (a *sparseArrayObject) setLengthInt(l int64, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(l, throw)
}
func (a *sparseArrayObject) setLength(v Value, throw bool) bool {
l, ok := toIntIgnoreNegZero(v)
if ok && l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
if ok {
return a._setLengthInt(l, throw)
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
}
func (a *sparseArrayObject) getIdx(idx int64, origNameStr string, origName Value) (v Value) {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value
}
if a.prototype != nil {
if origName != nil {
v = a.prototype.self.getProp(origName)
} else {
v = a.prototype.self.getPropStr(origNameStr)
}
}
return
}
func (a *sparseArrayObject) getProp(n Value) Value {
if idx := toIdx(n); idx >= 0 {
return a.getIdx(idx, "", n)
}
if n.String() == "length" {
return a.getLengthProp()
}
return a.baseObject.getProp(n)
}
func (a *sparseArrayObject) getLengthProp() Value {
a.lengthProp.value = intToValue(a.length)
return &a.lengthProp
}
func (a *sparseArrayObject) getOwnProp(name string) Value {
if idx := strToIdx(name); idx >= 0 {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value
}
return nil
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getOwnProp(name)
}
func (a *sparseArrayObject) getPropStr(name string) Value {
if i := strToIdx(name); i >= 0 {
return a.getIdx(i, name, nil)
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getPropStr(name)
}
func (a *sparseArrayObject) putIdx(idx int64, val Value, throw bool, origNameStr string, origName Value) {
var prop Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
prop = a.items[i].value
}
if prop == nil {
if a.prototype != nil {
var pprop Value
if origName != nil {
pprop = a.prototype.self.getProp(origName)
} else {
pprop = a.prototype.self.getPropStr(origNameStr)
}
if pprop, ok := pprop.(*valueProperty); ok {
if !pprop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return
}
if pprop.accessor {
pprop.set(a.val, val)
return
}
}
}
if !a.extensible {
a.val.runtime.typeErrorResult(throw)
return
}
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return
}
}
if a.expand() {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: val,
}
} else {
a.val.self.(*arrayObject).putIdx(idx, val, throw, origNameStr, origName)
return
}
} else {
if prop, ok := prop.(*valueProperty); ok {
if !prop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return
}
prop.set(a.val, val)
return
} else {
a.items[i].value = val
}
}
}
func (a *sparseArrayObject) put(n Value, val Value, throw bool) {
if idx := toIdx(n); idx >= 0 {
a.putIdx(idx, val, throw, "", n)
} else {
if n.String() == "length" {
a.setLength(val, throw)
} else {
a.baseObject.put(n, val, throw)
}
}
}
func (a *sparseArrayObject) putStr(name string, val Value, throw bool) {
if idx := strToIdx(name); idx >= 0 {
a.putIdx(idx, val, throw, name, nil)
} else {
if name == "length" {
a.setLength(val, throw)
} else {
a.baseObject.putStr(name, val, throw)
}
}
}
type sparseArrayPropIter struct {
a *sparseArrayObject
recursive bool
idx int
}
func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.items) {
name := strconv.Itoa(int(i.a.items[i.idx].idx))
prop := i.a.items[i.idx].value
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
return i.a.baseObject._enumerate(i.recursive)()
}
func (a *sparseArrayObject) _enumerate(recursive bool) iterNextFunc {
return (&sparseArrayPropIter{
a: a,
recursive: recursive,
}).next
}
func (a *sparseArrayObject) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: a._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (a *sparseArrayObject) setValues(values []Value) {
a.items = nil
for i, val := range values {
if val != nil {
a.items = append(a.items, sparseArrayItem{
idx: int64(i),
value: val,
})
}
}
}
func (a *sparseArrayObject) hasOwnProperty(n Value) bool {
if idx := toIdx(n); idx >= 0 {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value != _undefined
}
return false
} else {
return a.baseObject.hasOwnProperty(n)
}
}
func (a *sparseArrayObject) hasOwnPropertyStr(name string) bool {
if idx := strToIdx(name); idx >= 0 {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value != _undefined
}
return false
} else {
return a.baseObject.hasOwnPropertyStr(name)
}
}
func (a *sparseArrayObject) expand() bool {
if l := len(a.items); l >= 1024 {
if int(a.items[l-1].idx)/l < 8 {
//log.Println("Switching sparse->standard")
ar := &arrayObject{
baseObject: a.baseObject,
length: a.length,
propValueCount: a.propValueCount,
}
ar.setValuesFromSparse(a.items)
ar.val.self = ar
ar.init()
ar.lengthProp.writable = a.lengthProp.writable
return false
}
}
return true
}
func (a *sparseArrayObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
if idx := toIdx(n); idx >= 0 {
var existing Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
existing = a.items[i].value
}
prop, ok := a.baseObject._defineOwnProperty(n, existing, descr, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if i >= len(a.items) || a.items[i].idx != idx {
if a.expand() {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: prop,
}
if idx >= a.length {
a.length = idx + 1
}
} else {
return a.val.self.defineOwnProperty(n, descr, throw)
}
} else {
a.items[i].value = prop
}
if _, ok := prop.(*valueProperty); ok {
a.propValueCount++
}
}
return ok
} else {
if n.String() == "length" {
return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
}
return a.baseObject.defineOwnProperty(n, descr, throw)
}
}
func (a *sparseArrayObject) _deleteProp(idx int64, throw bool) bool {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
if p, ok := a.items[i].value.(*valueProperty); ok {
if !p.configurable {
a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.ToString())
return false
}
a.propValueCount--
}
copy(a.items[i:], a.items[i+1:])
a.items[len(a.items)-1].value = nil
a.items = a.items[:len(a.items)-1]
}
return true
}
func (a *sparseArrayObject) delete(n Value, throw bool) bool {
if idx := toIdx(n); idx >= 0 {
return a._deleteProp(idx, throw)
}
return a.baseObject.delete(n, throw)
}
func (a *sparseArrayObject) deleteStr(name string, throw bool) bool {
if idx := strToIdx(name); idx >= 0 {
return a._deleteProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
}
func (a *sparseArrayObject) sortLen() int64 {
if len(a.items) > 0 {
return a.items[len(a.items)-1].idx + 1
}
return 0
}
func (a *sparseArrayObject) sortGet(i int64) Value {
idx := a.findIdx(i)
if idx < len(a.items) && a.items[idx].idx == i {
v := a.items[idx].value
if p, ok := v.(*valueProperty); ok {
v = p.get(a.val)
}
return v
}
return nil
}
func (a *sparseArrayObject) swap(i, j int64) {
idxI := a.findIdx(i)
idxJ := a.findIdx(j)
if idxI < len(a.items) && a.items[idxI].idx == i && idxJ < len(a.items) && a.items[idxJ].idx == j {
a.items[idxI].value, a.items[idxJ].value = a.items[idxJ].value, a.items[idxI].value
}
}
func (a *sparseArrayObject) export() interface{} {
arr := make([]interface{}, a.length)
for _, item := range a.items {
if item.value != nil {
arr[item.idx] = item.value.Export()
}
}
return arr
}
func (a *sparseArrayObject) exportType() reflect.Type {
return reflectTypeArray
}

883
vendor/github.com/dop251/goja/builtin_array.go generated vendored Normal file
View file

@ -0,0 +1,883 @@
package goja
import (
"bytes"
"sort"
"strings"
)
func (r *Runtime) builtin_newArray(args []Value, proto *Object) *Object {
l := len(args)
if l == 1 {
if al, ok := args[0].assertInt(); ok {
return r.newArrayLength(al)
} else if f, ok := args[0].assertFloat(); ok {
al := int64(f)
if float64(al) == f {
return r.newArrayLength(al)
} else {
panic(r.newError(r.global.RangeError, "Invalid array length"))
}
}
return r.newArrayValues([]Value{args[0]})
} else {
argsCopy := make([]Value, l)
copy(argsCopy, args)
return r.newArrayValues(argsCopy)
}
}
func (r *Runtime) generic_push(obj *Object, call FunctionCall) Value {
l := toLength(obj.self.getStr("length"))
nl := l + int64(len(call.Arguments))
if nl >= maxInt {
r.typeErrorResult(true, "Invalid array length")
panic("unreachable")
}
for i, arg := range call.Arguments {
obj.self.put(intToValue(l+int64(i)), arg, true)
}
n := intToValue(nl)
obj.self.putStr("length", n, true)
return n
}
func (r *Runtime) arrayproto_push(call FunctionCall) Value {
obj := call.This.ToObject(r)
return r.generic_push(obj, call)
}
func (r *Runtime) arrayproto_pop_generic(obj *Object, call FunctionCall) Value {
l := toLength(obj.self.getStr("length"))
if l == 0 {
obj.self.putStr("length", intToValue(0), true)
return _undefined
}
idx := intToValue(l - 1)
val := obj.self.get(idx)
obj.self.delete(idx, true)
obj.self.putStr("length", idx, true)
return val
}
func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
obj := call.This.ToObject(r)
if a, ok := obj.self.(*arrayObject); ok {
l := a.length
if l > 0 {
var val Value
l--
if l < int64(len(a.values)) {
val = a.values[l]
}
if val == nil {
// optimisation bail-out
return r.arrayproto_pop_generic(obj, call)
}
if _, ok := val.(*valueProperty); ok {
// optimisation bail-out
return r.arrayproto_pop_generic(obj, call)
}
//a._setLengthInt(l, false)
a.values[l] = nil
a.values = a.values[:l]
a.length = l
return val
}
return _undefined
} else {
return r.arrayproto_pop_generic(obj, call)
}
}
func (r *Runtime) arrayproto_join(call FunctionCall) Value {
o := call.This.ToObject(r)
l := int(toLength(o.self.getStr("length")))
sep := ""
if s := call.Argument(0); s != _undefined {
sep = s.String()
} else {
sep = ","
}
if l == 0 {
return stringEmpty
}
var buf bytes.Buffer
element0 := o.self.get(intToValue(0))
if element0 != nil && element0 != _undefined && element0 != _null {
buf.WriteString(element0.String())
}
for i := 1; i < l; i++ {
buf.WriteString(sep)
element := o.self.get(intToValue(int64(i)))
if element != nil && element != _undefined && element != _null {
buf.WriteString(element.String())
}
}
return newStringValue(buf.String())
}
func (r *Runtime) arrayproto_toString(call FunctionCall) Value {
array := call.This.ToObject(r)
f := array.self.getStr("join")
if fObj, ok := f.(*Object); ok {
if fcall, ok := fObj.self.assertCallable(); ok {
return fcall(FunctionCall{
This: array,
})
}
}
return r.objectproto_toString(FunctionCall{
This: array,
})
}
func (r *Runtime) writeItemLocaleString(item Value, buf *bytes.Buffer) {
if item != nil && item != _undefined && item != _null {
itemObj := item.ToObject(r)
if f, ok := itemObj.self.getStr("toLocaleString").(*Object); ok {
if c, ok := f.self.assertCallable(); ok {
strVal := c(FunctionCall{
This: itemObj,
})
buf.WriteString(strVal.String())
return
}
}
r.typeErrorResult(true, "Property 'toLocaleString' of object %s is not a function", itemObj)
}
}
func (r *Runtime) arrayproto_toLocaleString_generic(obj *Object, start int64, buf *bytes.Buffer) Value {
length := toLength(obj.self.getStr("length"))
for i := int64(start); i < length; i++ {
if i > 0 {
buf.WriteByte(',')
}
item := obj.self.get(intToValue(i))
r.writeItemLocaleString(item, buf)
}
return newStringValue(buf.String())
}
func (r *Runtime) arrayproto_toLocaleString(call FunctionCall) Value {
array := call.This.ToObject(r)
if a, ok := array.self.(*arrayObject); ok {
var buf bytes.Buffer
for i := int64(0); i < a.length; i++ {
var item Value
if i < int64(len(a.values)) {
item = a.values[i]
}
if item == nil {
return r.arrayproto_toLocaleString_generic(array, i, &buf)
}
if prop, ok := item.(*valueProperty); ok {
item = prop.get(array)
}
if i > 0 {
buf.WriteByte(',')
}
r.writeItemLocaleString(item, &buf)
}
return newStringValue(buf.String())
} else {
return r.arrayproto_toLocaleString_generic(array, 0, bytes.NewBuffer(nil))
}
}
func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
descr := propertyDescr{
Writable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
Configurable: FLAG_TRUE,
}
aLength := toLength(a.self.getStr("length"))
if obj, ok := item.(*Object); ok {
if isArray(obj) {
length := toLength(obj.self.getStr("length"))
for i := int64(0); i < length; i++ {
v := obj.self.get(intToValue(i))
if v != nil {
descr.Value = v
a.self.defineOwnProperty(intToValue(aLength), descr, false)
aLength++
} else {
aLength++
a.self.putStr("length", intToValue(aLength), false)
}
}
return
}
}
descr.Value = item
a.self.defineOwnProperty(intToValue(aLength), descr, false)
}
func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
a := r.newArrayValues(nil)
r.arrayproto_concat_append(a, call.This.ToObject(r))
for _, item := range call.Arguments {
r.arrayproto_concat_append(a, item)
}
return a
}
func max(a, b int64) int64 {
if a > b {
return a
}
return b
}
func min(a, b int64) int64 {
if a < b {
return a
}
return b
}
func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
start := call.Argument(0).ToInteger()
if start < 0 {
start = max(length+start, 0)
} else {
start = min(start, length)
}
var end int64
if endArg := call.Argument(1); endArg != _undefined {
end = endArg.ToInteger()
} else {
end = length
}
if end < 0 {
end = max(length+end, 0)
} else {
end = min(end, length)
}
count := end - start
if count < 0 {
count = 0
}
a := r.newArrayLength(count)
n := int64(0)
descr := propertyDescr{
Writable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
Configurable: FLAG_TRUE,
}
for start < end {
p := o.self.get(intToValue(start))
if p != nil && p != _undefined {
descr.Value = p
a.self.defineOwnProperty(intToValue(n), descr, false)
}
start++
n++
}
return a
}
func (r *Runtime) arrayproto_sort(call FunctionCall) Value {
o := call.This.ToObject(r)
var compareFn func(FunctionCall) Value
if arg, ok := call.Argument(0).(*Object); ok {
compareFn, _ = arg.self.assertCallable()
}
ctx := arraySortCtx{
obj: o.self,
compare: compareFn,
}
sort.Sort(&ctx)
return o
}
func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
o := call.This.ToObject(r)
a := r.newArrayValues(nil)
length := toLength(o.self.getStr("length"))
relativeStart := call.Argument(0).ToInteger()
var actualStart int64
if relativeStart < 0 {
actualStart = max(length+relativeStart, 0)
} else {
actualStart = min(relativeStart, length)
}
actualDeleteCount := min(max(call.Argument(1).ToInteger(), 0), length-actualStart)
for k := int64(0); k < actualDeleteCount; k++ {
from := intToValue(k + actualStart)
if o.self.hasProperty(from) {
a.self.put(intToValue(k), o.self.get(from), false)
}
}
itemCount := max(int64(len(call.Arguments)-2), 0)
if itemCount < actualDeleteCount {
for k := actualStart; k < length-actualDeleteCount; k++ {
from := intToValue(k + actualDeleteCount)
to := intToValue(k + itemCount)
if o.self.hasProperty(from) {
o.self.put(to, o.self.get(from), true)
} else {
o.self.delete(to, true)
}
}
for k := length; k > length-actualDeleteCount+itemCount; k-- {
o.self.delete(intToValue(k-1), true)
}
} else if itemCount > actualDeleteCount {
for k := length - actualDeleteCount; k > actualStart; k-- {
from := intToValue(k + actualDeleteCount - 1)
to := intToValue(k + itemCount - 1)
if o.self.hasProperty(from) {
o.self.put(to, o.self.get(from), true)
} else {
o.self.delete(to, true)
}
}
}
if itemCount > 0 {
for i, item := range call.Arguments[2:] {
o.self.put(intToValue(actualStart+int64(i)), item, true)
}
}
o.self.putStr("length", intToValue(length-actualDeleteCount+itemCount), true)
return a
}
func (r *Runtime) arrayproto_unshift(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
argCount := int64(len(call.Arguments))
for k := length - 1; k >= 0; k-- {
from := intToValue(k)
to := intToValue(k + argCount)
if o.self.hasProperty(from) {
o.self.put(to, o.self.get(from), true)
} else {
o.self.delete(to, true)
}
}
for k, arg := range call.Arguments {
o.self.put(intToValue(int64(k)), arg, true)
}
newLen := intToValue(length + argCount)
o.self.putStr("length", newLen, true)
return newLen
}
func (r *Runtime) arrayproto_indexOf(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
if length == 0 {
return intToValue(-1)
}
n := call.Argument(1).ToInteger()
if n >= length {
return intToValue(-1)
}
if n < 0 {
n = max(length+n, 0)
}
searchElement := call.Argument(0)
for ; n < length; n++ {
idx := intToValue(n)
if val := o.self.get(idx); val != nil {
if searchElement.StrictEquals(val) {
return idx
}
}
}
return intToValue(-1)
}
func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
if length == 0 {
return intToValue(-1)
}
var fromIndex int64
if len(call.Arguments) < 2 {
fromIndex = length - 1
} else {
fromIndex = call.Argument(1).ToInteger()
if fromIndex >= 0 {
fromIndex = min(fromIndex, length-1)
} else {
fromIndex += length
}
}
searchElement := call.Argument(0)
for k := fromIndex; k >= 0; k-- {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
if searchElement.StrictEquals(val) {
return idx
}
}
}
return intToValue(-1)
}
func (r *Runtime) arrayproto_every(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: call.Argument(1),
Arguments: []Value{nil, nil, o},
}
for k := int64(0); k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
fc.Arguments[1] = idx
if !callbackFn(fc).ToBoolean() {
return valueFalse
}
}
}
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
return valueTrue
}
func (r *Runtime) arrayproto_some(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: call.Argument(1),
Arguments: []Value{nil, nil, o},
}
for k := int64(0); k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
fc.Arguments[1] = idx
if callbackFn(fc).ToBoolean() {
return valueTrue
}
}
}
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
return valueFalse
}
func (r *Runtime) arrayproto_forEach(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: call.Argument(1),
Arguments: []Value{nil, nil, o},
}
for k := int64(0); k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
fc.Arguments[1] = idx
callbackFn(fc)
}
}
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
return _undefined
}
func (r *Runtime) arrayproto_map(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: call.Argument(1),
Arguments: []Value{nil, nil, o},
}
a := r.newArrayObject()
a._setLengthInt(length, true)
a.values = make([]Value, length)
for k := int64(0); k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
fc.Arguments[1] = idx
a.values[k] = callbackFn(fc)
a.objCount++
}
}
return a.val
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
panic("unreachable")
}
func (r *Runtime) arrayproto_filter(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
a := r.newArrayObject()
fc := FunctionCall{
This: call.Argument(1),
Arguments: []Value{nil, nil, o},
}
for k := int64(0); k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
fc.Arguments[1] = idx
if callbackFn(fc).ToBoolean() {
a.values = append(a.values, val)
}
}
}
a.length = int64(len(a.values))
a.objCount = a.length
return a.val
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
panic("unreachable")
}
func (r *Runtime) arrayproto_reduce(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: _undefined,
Arguments: []Value{nil, nil, nil, o},
}
var k int64
if len(call.Arguments) >= 2 {
fc.Arguments[0] = call.Argument(1)
} else {
for ; k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
break
}
}
if fc.Arguments[0] == nil {
r.typeErrorResult(true, "No initial value")
panic("unreachable")
}
k++
}
for ; k < length; k++ {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[1] = val
fc.Arguments[2] = idx
fc.Arguments[0] = callbackFn(fc)
}
}
return fc.Arguments[0]
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
panic("unreachable")
}
func (r *Runtime) arrayproto_reduceRight(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
callbackFn := call.Argument(0).ToObject(r)
if callbackFn, ok := callbackFn.self.assertCallable(); ok {
fc := FunctionCall{
This: _undefined,
Arguments: []Value{nil, nil, nil, o},
}
k := length - 1
if len(call.Arguments) >= 2 {
fc.Arguments[0] = call.Argument(1)
} else {
for ; k >= 0; k-- {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[0] = val
break
}
}
if fc.Arguments[0] == nil {
r.typeErrorResult(true, "No initial value")
panic("unreachable")
}
k--
}
for ; k >= 0; k-- {
idx := intToValue(k)
if val := o.self.get(idx); val != nil {
fc.Arguments[1] = val
fc.Arguments[2] = idx
fc.Arguments[0] = callbackFn(fc)
}
}
return fc.Arguments[0]
} else {
r.typeErrorResult(true, "%s is not a function", call.Argument(0))
}
panic("unreachable")
}
func arrayproto_reverse_generic_step(o *Object, lower, upper int64) {
lowerP := intToValue(lower)
upperP := intToValue(upper)
lowerValue := o.self.get(lowerP)
upperValue := o.self.get(upperP)
if lowerValue != nil && upperValue != nil {
o.self.put(lowerP, upperValue, true)
o.self.put(upperP, lowerValue, true)
} else if lowerValue == nil && upperValue != nil {
o.self.put(lowerP, upperValue, true)
o.self.delete(upperP, true)
} else if lowerValue != nil && upperValue == nil {
o.self.delete(lowerP, true)
o.self.put(upperP, lowerValue, true)
}
}
func (r *Runtime) arrayproto_reverse_generic(o *Object, start int64) {
l := toLength(o.self.getStr("length"))
middle := l / 2
for lower := start; lower != middle; lower++ {
arrayproto_reverse_generic_step(o, lower, l-lower-1)
}
}
func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {
o := call.This.ToObject(r)
if a, ok := o.self.(*arrayObject); ok {
l := a.length
middle := l / 2
al := int64(len(a.values))
for lower := int64(0); lower != middle; lower++ {
upper := l - lower - 1
var lowerValue, upperValue Value
if upper >= al || lower >= al {
goto bailout
}
lowerValue = a.values[lower]
if lowerValue == nil {
goto bailout
}
if _, ok := lowerValue.(*valueProperty); ok {
goto bailout
}
upperValue = a.values[upper]
if upperValue == nil {
goto bailout
}
if _, ok := upperValue.(*valueProperty); ok {
goto bailout
}
a.values[lower], a.values[upper] = upperValue, lowerValue
continue
bailout:
arrayproto_reverse_generic_step(o, lower, upper)
}
//TODO: go arrays
} else {
r.arrayproto_reverse_generic(o, 0)
}
return o
}
func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
o := call.This.ToObject(r)
length := toLength(o.self.getStr("length"))
if length == 0 {
o.self.putStr("length", intToValue(0), true)
return _undefined
}
first := o.self.get(intToValue(0))
for i := int64(1); i < length; i++ {
v := o.self.get(intToValue(i))
if v != nil && v != _undefined {
o.self.put(intToValue(i-1), v, true)
} else {
o.self.delete(intToValue(i-1), true)
}
}
lv := intToValue(length - 1)
o.self.delete(lv, true)
o.self.putStr("length", lv, true)
return first
}
func (r *Runtime) array_isArray(call FunctionCall) Value {
if o, ok := call.Argument(0).(*Object); ok {
if isArray(o) {
return valueTrue
}
}
return valueFalse
}
func (r *Runtime) createArrayProto(val *Object) objectImpl {
o := &arrayObject{
baseObject: baseObject{
class: classArray,
val: val,
extensible: true,
prototype: r.global.ObjectPrototype,
},
}
o.init()
o._putProp("constructor", r.global.Array, true, false, true)
o._putProp("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), true, false, true)
o._putProp("push", r.newNativeFunc(r.arrayproto_push, nil, "push", nil, 1), true, false, true)
o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
o._putProp("toString", r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", nil, 1), true, false, true)
o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true)
o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true)
o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true)
o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true)
o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true)
o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true)
o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
o._putProp("reduce", r.newNativeFunc(r.arrayproto_reduce, nil, "reduce", nil, 1), true, false, true)
o._putProp("reduceRight", r.newNativeFunc(r.arrayproto_reduceRight, nil, "reduceRight", nil, 1), true, false, true)
return o
}
func (r *Runtime) createArray(val *Object) objectImpl {
o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
return o
}
func (r *Runtime) initArray() {
//r.global.ArrayPrototype = r.newArray(r.global.ObjectPrototype).val
//o := r.global.ArrayPrototype.self
r.global.ArrayPrototype = r.newLazyObject(r.createArrayProto)
//r.global.Array = r.newNativeFuncConstruct(r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
//o = r.global.Array.self
//o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
r.global.Array = r.newLazyObject(r.createArray)
r.addToGlobal("Array", r.global.Array)
}
type sortable interface {
sortLen() int64
sortGet(int64) Value
swap(int64, int64)
}
type arraySortCtx struct {
obj sortable
compare func(FunctionCall) Value
}
func (ctx *arraySortCtx) sortCompare(x, y Value) int {
if x == nil && y == nil {
return 0
}
if x == nil {
return 1
}
if y == nil {
return -1
}
if x == _undefined && y == _undefined {
return 0
}
if x == _undefined {
return 1
}
if y == _undefined {
return -1
}
if ctx.compare != nil {
return int(ctx.compare(FunctionCall{
This: _undefined,
Arguments: []Value{x, y},
}).ToInteger())
}
return strings.Compare(x.String(), y.String())
}
// sort.Interface
func (a *arraySortCtx) Len() int {
return int(a.obj.sortLen())
}
func (a *arraySortCtx) Less(j, k int) bool {
return a.sortCompare(a.obj.sortGet(int64(j)), a.obj.sortGet(int64(k))) < 0
}
func (a *arraySortCtx) Swap(j, k int) {
a.obj.swap(int64(j), int64(k))
}

50
vendor/github.com/dop251/goja/builtin_boolean.go generated vendored Normal file
View file

@ -0,0 +1,50 @@
package goja
func (r *Runtime) booleanproto_toString(call FunctionCall) Value {
var b bool
switch o := call.This.(type) {
case valueBool:
b = bool(o)
goto success
case *Object:
if p, ok := o.self.(*primitiveValueObject); ok {
if b1, ok := p.pValue.(valueBool); ok {
b = bool(b1)
goto success
}
}
}
r.typeErrorResult(true, "Method Boolean.prototype.toString is called on incompatible receiver")
success:
if b {
return stringTrue
}
return stringFalse
}
func (r *Runtime) booleanproto_valueOf(call FunctionCall) Value {
switch o := call.This.(type) {
case valueBool:
return o
case *Object:
if p, ok := o.self.(*primitiveValueObject); ok {
if b, ok := p.pValue.(valueBool); ok {
return b
}
}
}
r.typeErrorResult(true, "Method Boolean.prototype.valueOf is called on incompatible receiver")
return nil
}
func (r *Runtime) initBoolean() {
r.global.BooleanPrototype = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean)
o := r.global.BooleanPrototype.self
o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
r.global.Boolean = r.newNativeFunc(r.builtin_Boolean, r.builtin_newBoolean, "Boolean", r.global.BooleanPrototype, 1)
r.addToGlobal("Boolean", r.global.Boolean)
}

933
vendor/github.com/dop251/goja/builtin_date.go generated vendored Normal file
View file

@ -0,0 +1,933 @@
package goja
import (
"fmt"
"math"
"time"
)
const (
maxTime = 8.64e15
)
func timeFromMsec(msec int64) time.Time {
sec := msec / 1000
nsec := (msec % 1000) * 1e6
return time.Unix(sec, nsec)
}
func timeToMsec(t time.Time) int64 {
return t.Unix()*1000 + int64(t.Nanosecond())/1e6
}
func (r *Runtime) makeDate(args []Value, loc *time.Location) (t time.Time, valid bool) {
pick := func(index int, default_ int64) (int64, bool) {
if index >= len(args) {
return default_, true
}
value := args[index]
if valueInt, ok := value.assertInt(); ok {
return valueInt, true
}
valueFloat := value.ToFloat()
if math.IsNaN(valueFloat) || math.IsInf(valueFloat, 0) {
return 0, false
}
return int64(valueFloat), true
}
switch {
case len(args) >= 2:
var year, month, day, hour, minute, second, millisecond int64
if year, valid = pick(0, 1900); !valid {
return
}
if month, valid = pick(1, 0); !valid {
return
}
if day, valid = pick(2, 1); !valid {
return
}
if hour, valid = pick(3, 0); !valid {
return
}
if minute, valid = pick(4, 0); !valid {
return
}
if second, valid = pick(5, 0); !valid {
return
}
if millisecond, valid = pick(6, 0); !valid {
return
}
if year >= 0 && year <= 99 {
year += 1900
}
t = time.Date(int(year), time.Month(int(month)+1), int(day), int(hour), int(minute), int(second), int(millisecond)*1e6, loc)
case len(args) == 0:
t = r.now()
valid = true
default: // one argument
pv := toPrimitiveNumber(args[0])
if val, ok := pv.assertString(); ok {
return dateParse(val.String())
}
var n int64
if i, ok := pv.assertInt(); ok {
n = i
} else if f, ok := pv.assertFloat(); ok {
if math.IsNaN(f) || math.IsInf(f, 0) {
return
}
if math.Abs(f) > maxTime {
return
}
n = int64(f)
} else {
n = pv.ToInteger()
}
t = timeFromMsec(n)
valid = true
}
msec := t.Unix()*1000 + int64(t.Nanosecond()/1e6)
if msec < 0 {
msec = -msec
}
if msec > maxTime {
valid = false
}
return
}
func (r *Runtime) newDateTime(args []Value, loc *time.Location) *Object {
t, isSet := r.makeDate(args, loc)
return r.newDateObject(t, isSet)
}
func (r *Runtime) builtin_newDate(args []Value) *Object {
return r.newDateTime(args, time.Local)
}
func (r *Runtime) builtin_date(call FunctionCall) Value {
return asciiString(dateFormat(r.now()))
}
func (r *Runtime) date_parse(call FunctionCall) Value {
t, set := dateParse(call.Argument(0).String())
if set {
return intToValue(timeToMsec(t))
}
return _NaN
}
func (r *Runtime) date_UTC(call FunctionCall) Value {
t, valid := r.makeDate(call.Arguments, time.UTC)
if !valid {
return _NaN
}
return intToValue(timeToMsec(t))
}
func (r *Runtime) date_now(call FunctionCall) Value {
return intToValue(timeToMsec(r.now()))
}
func (r *Runtime) dateproto_toString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(dateTimeLayout))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toUTCString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.In(time.UTC).Format(dateTimeLayout))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toUTCString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toISOString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
utc := d.time.In(time.UTC)
year := utc.Year()
if year >= -9999 && year <= 9999 {
return asciiString(utc.Format(isoDateTimeLayout))
}
// extended year
return asciiString(fmt.Sprintf("%+06d-", year) + utc.Format(isoDateTimeLayout[5:]))
} else {
panic(r.newError(r.global.RangeError, "Invalid time value"))
}
}
r.typeErrorResult(true, "Method Date.prototype.toISOString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toJSON(call FunctionCall) Value {
obj := r.toObject(call.This)
tv := obj.self.toPrimitiveNumber()
if f, ok := tv.assertFloat(); ok {
if math.IsNaN(f) || math.IsInf(f, 0) {
return _null
}
} else if _, ok := tv.assertInt(); !ok {
return _null
}
if toISO, ok := obj.self.getStr("toISOString").(*Object); ok {
if toISO, ok := toISO.self.assertCallable(); ok {
return toISO(FunctionCall{
This: obj,
})
}
}
r.typeErrorResult(true, "toISOString is not a function")
panic("Unreachable")
}
func (r *Runtime) dateproto_toDateString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(dateLayout))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toDateString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toTimeString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(timeLayout))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toTimeString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toLocaleString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(datetimeLayout_en_GB))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toLocaleString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toLocaleDateString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(dateLayout_en_GB))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toLocaleDateString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_toLocaleTimeString(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return asciiString(d.time.Format(timeLayout_en_GB))
} else {
return stringInvalidDate
}
}
r.typeErrorResult(true, "Method Date.prototype.toLocaleTimeString is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_valueOf(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(d.time.Unix()*1000 + int64(d.time.Nanosecond()/1e6))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.valueOf is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getTime(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getTime is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getFullYear(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Year()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getFullYear is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCFullYear(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Year()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCFullYear is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getMonth(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Month()) - 1)
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getMonth is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCMonth(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Month()) - 1)
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCMonth is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getHours(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Hour()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getHours is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCHours(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Hour()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCHours is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getDate(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Day()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getDate is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCDate(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Day()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCDate is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getDay(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Weekday()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getDay is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCDay(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Weekday()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCDay is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getMinutes(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Minute()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getMinutes is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCMinutes(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Minute()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCMinutes is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getSeconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Second()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getSeconds is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCSeconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Second()))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCSeconds is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getMilliseconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.Nanosecond() / 1e6))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getMilliseconds is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getUTCMilliseconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
return intToValue(int64(d.time.In(time.UTC).Nanosecond() / 1e6))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getUTCMilliseconds is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_getTimezoneOffset(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
_, offset := d.time.Zone()
return floatToValue(float64(-offset) / 60)
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.getTimezoneOffset is called on incompatible receiver")
return nil
}
func (r *Runtime) dateproto_setTime(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
msec := call.Argument(0).ToInteger()
d.time = timeFromMsec(msec)
return intToValue(msec)
}
r.typeErrorResult(true, "Method Date.prototype.setTime is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setMilliseconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
msec := call.Argument(0).ToInteger()
m := timeToMsec(d.time) - int64(d.time.Nanosecond())/1e6 + msec
d.time = timeFromMsec(m)
return intToValue(m)
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setMilliseconds is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCMilliseconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
msec := call.Argument(0).ToInteger()
m := timeToMsec(d.time) - int64(d.time.Nanosecond())/1e6 + msec
d.time = timeFromMsec(m)
return intToValue(m)
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCMilliseconds is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setSeconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
sec := int(call.Argument(0).ToInteger())
var nsec int
if len(call.Arguments) > 1 {
nsec = int(call.Arguments[1].ToInteger() * 1e6)
} else {
nsec = d.time.Nanosecond()
}
d.time = time.Date(d.time.Year(), d.time.Month(), d.time.Day(), d.time.Hour(), d.time.Minute(), sec, nsec, time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setSeconds is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCSeconds(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
sec := int(call.Argument(0).ToInteger())
var nsec int
t := d.time.In(time.UTC)
if len(call.Arguments) > 1 {
nsec = int(call.Arguments[1].ToInteger() * 1e6)
} else {
nsec = t.Nanosecond()
}
d.time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), sec, nsec, time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCSeconds is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setMinutes(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
min := int(call.Argument(0).ToInteger())
var sec, nsec int
if len(call.Arguments) > 1 {
sec = int(call.Arguments[1].ToInteger())
} else {
sec = d.time.Second()
}
if len(call.Arguments) > 2 {
nsec = int(call.Arguments[2].ToInteger() * 1e6)
} else {
nsec = d.time.Nanosecond()
}
d.time = time.Date(d.time.Year(), d.time.Month(), d.time.Day(), d.time.Hour(), min, sec, nsec, time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setMinutes is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCMinutes(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
min := int(call.Argument(0).ToInteger())
var sec, nsec int
t := d.time.In(time.UTC)
if len(call.Arguments) > 1 {
sec = int(call.Arguments[1].ToInteger())
} else {
sec = t.Second()
}
if len(call.Arguments) > 2 {
nsec = int(call.Arguments[2].ToInteger() * 1e6)
} else {
nsec = t.Nanosecond()
}
d.time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), min, sec, nsec, time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCMinutes is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setHours(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
hour := int(call.Argument(0).ToInteger())
var min, sec, nsec int
if len(call.Arguments) > 1 {
min = int(call.Arguments[1].ToInteger())
} else {
min = d.time.Minute()
}
if len(call.Arguments) > 2 {
sec = int(call.Arguments[2].ToInteger())
} else {
sec = d.time.Second()
}
if len(call.Arguments) > 3 {
nsec = int(call.Arguments[3].ToInteger() * 1e6)
} else {
nsec = d.time.Nanosecond()
}
d.time = time.Date(d.time.Year(), d.time.Month(), d.time.Day(), hour, min, sec, nsec, time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setHours is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCHours(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
hour := int(call.Argument(0).ToInteger())
var min, sec, nsec int
t := d.time.In(time.UTC)
if len(call.Arguments) > 1 {
min = int(call.Arguments[1].ToInteger())
} else {
min = t.Minute()
}
if len(call.Arguments) > 2 {
sec = int(call.Arguments[2].ToInteger())
} else {
sec = t.Second()
}
if len(call.Arguments) > 3 {
nsec = int(call.Arguments[3].ToInteger() * 1e6)
} else {
nsec = t.Nanosecond()
}
d.time = time.Date(d.time.Year(), d.time.Month(), d.time.Day(), hour, min, sec, nsec, time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCHours is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setDate(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
d.time = time.Date(d.time.Year(), d.time.Month(), int(call.Argument(0).ToInteger()), d.time.Hour(), d.time.Minute(), d.time.Second(), d.time.Nanosecond(), time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setDate is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCDate(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
t := d.time.In(time.UTC)
d.time = time.Date(t.Year(), t.Month(), int(call.Argument(0).ToInteger()), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCDate is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setMonth(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
month := time.Month(int(call.Argument(0).ToInteger()) + 1)
var day int
if len(call.Arguments) > 1 {
day = int(call.Arguments[1].ToInteger())
} else {
day = d.time.Day()
}
d.time = time.Date(d.time.Year(), month, day, d.time.Hour(), d.time.Minute(), d.time.Second(), d.time.Nanosecond(), time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setMonth is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCMonth(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if d.isSet {
month := time.Month(int(call.Argument(0).ToInteger()) + 1)
var day int
t := d.time.In(time.UTC)
if len(call.Arguments) > 1 {
day = int(call.Arguments[1].ToInteger())
} else {
day = t.Day()
}
d.time = time.Date(t.Year(), month, day, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
} else {
return _NaN
}
}
r.typeErrorResult(true, "Method Date.prototype.setUTCMonth is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setFullYear(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if !d.isSet {
d.time = time.Unix(0, 0)
}
year := int(call.Argument(0).ToInteger())
var month time.Month
var day int
if len(call.Arguments) > 1 {
month = time.Month(call.Arguments[1].ToInteger() + 1)
} else {
month = d.time.Month()
}
if len(call.Arguments) > 2 {
day = int(call.Arguments[2].ToInteger())
} else {
day = d.time.Day()
}
d.time = time.Date(year, month, day, d.time.Hour(), d.time.Minute(), d.time.Second(), d.time.Nanosecond(), time.Local)
return intToValue(timeToMsec(d.time))
}
r.typeErrorResult(true, "Method Date.prototype.setFullYear is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) dateproto_setUTCFullYear(call FunctionCall) Value {
obj := r.toObject(call.This)
if d, ok := obj.self.(*dateObject); ok {
if !d.isSet {
d.time = time.Unix(0, 0)
}
year := int(call.Argument(0).ToInteger())
var month time.Month
var day int
t := d.time.In(time.UTC)
if len(call.Arguments) > 1 {
month = time.Month(call.Arguments[1].ToInteger() + 1)
} else {
month = t.Month()
}
if len(call.Arguments) > 2 {
day = int(call.Arguments[2].ToInteger())
} else {
day = t.Day()
}
d.time = time.Date(year, month, day, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC).In(time.Local)
return intToValue(timeToMsec(d.time))
}
r.typeErrorResult(true, "Method Date.prototype.setUTCFullYear is called on incompatible receiver")
panic("Unreachable")
}
func (r *Runtime) createDateProto(val *Object) objectImpl {
o := &baseObject{
class: classObject,
val: val,
extensible: true,
prototype: r.global.ObjectPrototype,
}
o.init()
o._putProp("constructor", r.global.Date, true, false, true)
o._putProp("toString", r.newNativeFunc(r.dateproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("toDateString", r.newNativeFunc(r.dateproto_toDateString, nil, "toDateString", nil, 0), true, false, true)
o._putProp("toTimeString", r.newNativeFunc(r.dateproto_toTimeString, nil, "toTimeString", nil, 0), true, false, true)
o._putProp("toLocaleString", r.newNativeFunc(r.dateproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
o._putProp("toLocaleDateString", r.newNativeFunc(r.dateproto_toLocaleDateString, nil, "toLocaleDateString", nil, 0), true, false, true)
o._putProp("toLocaleTimeString", r.newNativeFunc(r.dateproto_toLocaleTimeString, nil, "toLocaleTimeString", nil, 0), true, false, true)
o._putProp("valueOf", r.newNativeFunc(r.dateproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
o._putProp("getTime", r.newNativeFunc(r.dateproto_getTime, nil, "getTime", nil, 0), true, false, true)
o._putProp("getFullYear", r.newNativeFunc(r.dateproto_getFullYear, nil, "getFullYear", nil, 0), true, false, true)
o._putProp("getUTCFullYear", r.newNativeFunc(r.dateproto_getUTCFullYear, nil, "getUTCFullYear", nil, 0), true, false, true)
o._putProp("getMonth", r.newNativeFunc(r.dateproto_getMonth, nil, "getMonth", nil, 0), true, false, true)
o._putProp("getUTCMonth", r.newNativeFunc(r.dateproto_getUTCMonth, nil, "getUTCMonth", nil, 0), true, false, true)
o._putProp("getDate", r.newNativeFunc(r.dateproto_getDate, nil, "getDate", nil, 0), true, false, true)
o._putProp("getUTCDate", r.newNativeFunc(r.dateproto_getUTCDate, nil, "getUTCDate", nil, 0), true, false, true)
o._putProp("getDay", r.newNativeFunc(r.dateproto_getDay, nil, "getDay", nil, 0), true, false, true)
o._putProp("getUTCDay", r.newNativeFunc(r.dateproto_getUTCDay, nil, "getUTCDay", nil, 0), true, false, true)
o._putProp("getHours", r.newNativeFunc(r.dateproto_getHours, nil, "getHours", nil, 0), true, false, true)
o._putProp("getUTCHours", r.newNativeFunc(r.dateproto_getUTCHours, nil, "getUTCHours", nil, 0), true, false, true)
o._putProp("getMinutes", r.newNativeFunc(r.dateproto_getMinutes, nil, "getMinutes", nil, 0), true, false, true)
o._putProp("getUTCMinutes", r.newNativeFunc(r.dateproto_getUTCMinutes, nil, "getUTCMinutes", nil, 0), true, false, true)
o._putProp("getSeconds", r.newNativeFunc(r.dateproto_getSeconds, nil, "getSeconds", nil, 0), true, false, true)
o._putProp("getUTCSeconds", r.newNativeFunc(r.dateproto_getUTCSeconds, nil, "getUTCSeconds", nil, 0), true, false, true)
o._putProp("getMilliseconds", r.newNativeFunc(r.dateproto_getMilliseconds, nil, "getMilliseconds", nil, 0), true, false, true)
o._putProp("getUTCMilliseconds", r.newNativeFunc(r.dateproto_getUTCMilliseconds, nil, "getUTCMilliseconds", nil, 0), true, false, true)
o._putProp("getTimezoneOffset", r.newNativeFunc(r.dateproto_getTimezoneOffset, nil, "getTimezoneOffset", nil, 0), true, false, true)
o._putProp("setTime", r.newNativeFunc(r.dateproto_setTime, nil, "setTime", nil, 1), true, false, true)
o._putProp("setMilliseconds", r.newNativeFunc(r.dateproto_setMilliseconds, nil, "setMilliseconds", nil, 1), true, false, true)
o._putProp("setUTCMilliseconds", r.newNativeFunc(r.dateproto_setUTCMilliseconds, nil, "setUTCMilliseconds", nil, 1), true, false, true)
o._putProp("setSeconds", r.newNativeFunc(r.dateproto_setSeconds, nil, "setSeconds", nil, 2), true, false, true)
o._putProp("setUTCSeconds", r.newNativeFunc(r.dateproto_setUTCSeconds, nil, "setUTCSeconds", nil, 2), true, false, true)
o._putProp("setMinutes", r.newNativeFunc(r.dateproto_setMinutes, nil, "setMinutes", nil, 3), true, false, true)
o._putProp("setUTCMinutes", r.newNativeFunc(r.dateproto_setUTCMinutes, nil, "setUTCMinutes", nil, 3), true, false, true)
o._putProp("setHours", r.newNativeFunc(r.dateproto_setHours, nil, "setHours", nil, 4), true, false, true)
o._putProp("setUTCHours", r.newNativeFunc(r.dateproto_setUTCHours, nil, "setUTCHours", nil, 4), true, false, true)
o._putProp("setDate", r.newNativeFunc(r.dateproto_setDate, nil, "setDate", nil, 1), true, false, true)
o._putProp("setUTCDate", r.newNativeFunc(r.dateproto_setUTCDate, nil, "setUTCDate", nil, 1), true, false, true)
o._putProp("setMonth", r.newNativeFunc(r.dateproto_setMonth, nil, "setMonth", nil, 2), true, false, true)
o._putProp("setUTCMonth", r.newNativeFunc(r.dateproto_setUTCMonth, nil, "setUTCMonth", nil, 2), true, false, true)
o._putProp("setFullYear", r.newNativeFunc(r.dateproto_setFullYear, nil, "setFullYear", nil, 3), true, false, true)
o._putProp("setUTCFullYear", r.newNativeFunc(r.dateproto_setUTCFullYear, nil, "setUTCFullYear", nil, 3), true, false, true)
o._putProp("toUTCString", r.newNativeFunc(r.dateproto_toUTCString, nil, "toUTCString", nil, 0), true, false, true)
o._putProp("toISOString", r.newNativeFunc(r.dateproto_toISOString, nil, "toISOString", nil, 0), true, false, true)
o._putProp("toJSON", r.newNativeFunc(r.dateproto_toJSON, nil, "toJSON", nil, 1), true, false, true)
return o
}
func (r *Runtime) createDate(val *Object) objectImpl {
o := r.newNativeFuncObj(val, r.builtin_date, r.builtin_newDate, "Date", r.global.DatePrototype, 7)
o._putProp("parse", r.newNativeFunc(r.date_parse, nil, "parse", nil, 1), true, false, true)
o._putProp("UTC", r.newNativeFunc(r.date_UTC, nil, "UTC", nil, 7), true, false, true)
o._putProp("now", r.newNativeFunc(r.date_now, nil, "now", nil, 0), true, false, true)
return o
}
func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object {
val := &Object{runtime: r}
o := &lazyObject{
val: val,
create: create,
}
val.self = o
return val
}
func (r *Runtime) initDate() {
//r.global.DatePrototype = r.newObject()
//o := r.global.DatePrototype.self
r.global.DatePrototype = r.newLazyObject(r.createDateProto)
//r.global.Date = r.newNativeFunc(r.builtin_date, r.builtin_newDate, "Date", r.global.DatePrototype, 7)
//o := r.global.Date.self
r.global.Date = r.newLazyObject(r.createDate)
r.addToGlobal("Date", r.global.Date)
}

62
vendor/github.com/dop251/goja/builtin_error.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
package goja
func (r *Runtime) initErrors() {
r.global.ErrorPrototype = r.NewObject()
o := r.global.ErrorPrototype.self
o._putProp("message", stringEmpty, true, false, true)
o._putProp("name", stringError, true, false, true)
o._putProp("toString", r.newNativeFunc(r.error_toString, nil, "toString", nil, 0), true, false, true)
r.global.Error = r.newNativeFuncConstruct(r.builtin_Error, "Error", r.global.ErrorPrototype, 1)
o = r.global.Error.self
r.addToGlobal("Error", r.global.Error)
r.global.TypeErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.TypeErrorPrototype.self
o._putProp("name", stringTypeError, true, false, true)
r.global.TypeError = r.newNativeFuncConstructProto(r.builtin_Error, "TypeError", r.global.TypeErrorPrototype, r.global.Error, 1)
r.addToGlobal("TypeError", r.global.TypeError)
r.global.ReferenceErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.ReferenceErrorPrototype.self
o._putProp("name", stringReferenceError, true, false, true)
r.global.ReferenceError = r.newNativeFuncConstructProto(r.builtin_Error, "ReferenceError", r.global.ReferenceErrorPrototype, r.global.Error, 1)
r.addToGlobal("ReferenceError", r.global.ReferenceError)
r.global.SyntaxErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.SyntaxErrorPrototype.self
o._putProp("name", stringSyntaxError, true, false, true)
r.global.SyntaxError = r.newNativeFuncConstructProto(r.builtin_Error, "SyntaxError", r.global.SyntaxErrorPrototype, r.global.Error, 1)
r.addToGlobal("SyntaxError", r.global.SyntaxError)
r.global.RangeErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.RangeErrorPrototype.self
o._putProp("name", stringRangeError, true, false, true)
r.global.RangeError = r.newNativeFuncConstructProto(r.builtin_Error, "RangeError", r.global.RangeErrorPrototype, r.global.Error, 1)
r.addToGlobal("RangeError", r.global.RangeError)
r.global.EvalErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.EvalErrorPrototype.self
o._putProp("name", stringEvalError, true, false, true)
r.global.EvalError = r.newNativeFuncConstructProto(r.builtin_Error, "EvalError", r.global.EvalErrorPrototype, r.global.Error, 1)
r.addToGlobal("EvalError", r.global.EvalError)
r.global.URIErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.URIErrorPrototype.self
o._putProp("name", stringURIError, true, false, true)
r.global.URIError = r.newNativeFuncConstructProto(r.builtin_Error, "URIError", r.global.URIErrorPrototype, r.global.Error, 1)
r.addToGlobal("URIError", r.global.URIError)
r.global.GoErrorPrototype = r.builtin_new(r.global.Error, []Value{})
o = r.global.GoErrorPrototype.self
o._putProp("name", stringGoError, true, false, true)
r.global.GoError = r.newNativeFuncConstructProto(r.builtin_Error, "GoError", r.global.GoErrorPrototype, r.global.Error, 1)
r.addToGlobal("GoError", r.global.GoError)
}

165
vendor/github.com/dop251/goja/builtin_function.go generated vendored Normal file
View file

@ -0,0 +1,165 @@
package goja
import (
"fmt"
)
func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object {
src := "(function anonymous("
if len(args) > 1 {
for _, arg := range args[:len(args)-1] {
src += arg.String() + ","
}
src = src[:len(src)-1]
}
body := ""
if len(args) > 0 {
body = args[len(args)-1].String()
}
src += "){" + body + "})"
return r.toObject(r.eval(src, false, false, _undefined))
}
func (r *Runtime) functionproto_toString(call FunctionCall) Value {
obj := r.toObject(call.This)
repeat:
switch f := obj.self.(type) {
case *funcObject:
return newStringValue(f.src)
case *nativeFuncObject:
return newStringValue(fmt.Sprintf("function %s() { [native code] }", f.nameProp.get(call.This).ToString()))
case *boundFuncObject:
return newStringValue(fmt.Sprintf("function %s() { [native code] }", f.nameProp.get(call.This).ToString()))
case *lazyObject:
obj.self = f.create(obj)
goto repeat
}
r.typeErrorResult(true, "Object is not a function")
return nil
}
func (r *Runtime) toValueArray(a Value) []Value {
obj := r.toObject(a)
l := toUInt32(obj.self.getStr("length"))
ret := make([]Value, l)
for i := uint32(0); i < l; i++ {
ret[i] = obj.self.get(valueInt(i))
}
return ret
}
func (r *Runtime) functionproto_apply(call FunctionCall) Value {
f := r.toCallable(call.This)
var args []Value
if len(call.Arguments) >= 2 {
args = r.toValueArray(call.Arguments[1])
}
return f(FunctionCall{
This: call.Argument(0),
Arguments: args,
})
}
func (r *Runtime) functionproto_call(call FunctionCall) Value {
f := r.toCallable(call.This)
var args []Value
if len(call.Arguments) > 0 {
args = call.Arguments[1:]
}
return f(FunctionCall{
This: call.Argument(0),
Arguments: args,
})
}
func (r *Runtime) boundCallable(target func(FunctionCall) Value, boundArgs []Value) func(FunctionCall) Value {
var this Value
var args []Value
if len(boundArgs) > 0 {
this = boundArgs[0]
args = make([]Value, len(boundArgs)-1)
copy(args, boundArgs[1:])
} else {
this = _undefined
}
return func(call FunctionCall) Value {
a := append(args, call.Arguments...)
return target(FunctionCall{
This: this,
Arguments: a,
})
}
}
func (r *Runtime) boundConstruct(target func([]Value) *Object, boundArgs []Value) func([]Value) *Object {
if target == nil {
return nil
}
var args []Value
if len(boundArgs) > 1 {
args = make([]Value, len(boundArgs)-1)
copy(args, boundArgs[1:])
}
return func(fargs []Value) *Object {
a := append(args, fargs...)
copy(a, args)
return target(a)
}
}
func (r *Runtime) functionproto_bind(call FunctionCall) Value {
obj := r.toObject(call.This)
f := obj.self
var fcall func(FunctionCall) Value
var construct func([]Value) *Object
repeat:
switch ff := f.(type) {
case *funcObject:
fcall = ff.Call
construct = ff.construct
case *nativeFuncObject:
fcall = ff.f
construct = ff.construct
case *boundFuncObject:
f = &ff.nativeFuncObject
goto repeat
case *lazyObject:
f = ff.create(obj)
goto repeat
default:
r.typeErrorResult(true, "Value is not callable: %s", obj.ToString())
}
l := int(toUInt32(obj.self.getStr("length")))
l -= len(call.Arguments) - 1
if l < 0 {
l = 0
}
v := &Object{runtime: r}
ff := r.newNativeFuncObj(v, r.boundCallable(fcall, call.Arguments), r.boundConstruct(construct, call.Arguments), "", nil, l)
v.self = &boundFuncObject{
nativeFuncObject: *ff,
}
//ret := r.newNativeFunc(r.boundCallable(f, call.Arguments), nil, "", nil, l)
//o := ret.self
//o.putStr("caller", r.global.throwerProperty, false)
//o.putStr("arguments", r.global.throwerProperty, false)
return v
}
func (r *Runtime) initFunction() {
o := r.global.FunctionPrototype.self
o.(*nativeFuncObject).prototype = r.global.ObjectPrototype
o._putProp("toString", r.newNativeFunc(r.functionproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("apply", r.newNativeFunc(r.functionproto_apply, nil, "apply", nil, 2), true, false, true)
o._putProp("call", r.newNativeFunc(r.functionproto_call, nil, "call", nil, 1), true, false, true)
o._putProp("bind", r.newNativeFunc(r.functionproto_bind, nil, "bind", nil, 1), true, false, true)
r.global.Function = r.newNativeFuncConstruct(r.builtin_Function, "Function", r.global.FunctionPrototype, 1)
r.addToGlobal("Function", r.global.Function)
}

422
vendor/github.com/dop251/goja/builtin_global.go generated vendored Normal file
View file

@ -0,0 +1,422 @@
package goja
import (
"errors"
"io"
"math"
"regexp"
"strconv"
"unicode/utf16"
"unicode/utf8"
)
var (
parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`)
)
func (r *Runtime) builtin_isNaN(call FunctionCall) Value {
if math.IsNaN(call.Argument(0).ToFloat()) {
return valueTrue
} else {
return valueFalse
}
}
func (r *Runtime) builtin_parseInt(call FunctionCall) Value {
str := call.Argument(0).ToString().toTrimmedUTF8()
radix := int(toInt32(call.Argument(1)))
v, _ := parseInt(str, radix)
return v
}
func (r *Runtime) builtin_parseFloat(call FunctionCall) Value {
m := parseFloatRegexp.FindStringSubmatch(call.Argument(0).ToString().toTrimmedUTF8())
if len(m) == 2 {
if s := m[1]; s != "" && s != "+" && s != "-" {
switch s {
case "+", "-":
case "Infinity", "+Infinity":
return _positiveInf
case "-Infinity":
return _negativeInf
default:
f, err := strconv.ParseFloat(s, 64)
if err == nil || isRangeErr(err) {
return floatToValue(f)
}
}
}
}
return _NaN
}
func (r *Runtime) builtin_isFinite(call FunctionCall) Value {
f := call.Argument(0).ToFloat()
if math.IsNaN(f) || math.IsInf(f, 0) {
return valueFalse
}
return valueTrue
}
func (r *Runtime) _encode(uriString valueString, unescaped *[256]bool) valueString {
reader := uriString.reader(0)
utf8Buf := make([]byte, utf8.UTFMax)
needed := false
l := 0
for {
rn, _, err := reader.ReadRune()
if err != nil {
if err != io.EOF {
panic(r.newError(r.global.URIError, "Malformed URI"))
}
break
}
if rn >= utf8.RuneSelf {
needed = true
l += utf8.EncodeRune(utf8Buf, rn) * 3
} else if !unescaped[rn] {
needed = true
l += 3
} else {
l++
}
}
if !needed {
return uriString
}
buf := make([]byte, l)
i := 0
reader = uriString.reader(0)
for {
rn, _, err := reader.ReadRune()
if err != nil {
break
}
if rn >= utf8.RuneSelf {
n := utf8.EncodeRune(utf8Buf, rn)
for _, b := range utf8Buf[:n] {
buf[i] = '%'
buf[i+1] = "0123456789ABCDEF"[b>>4]
buf[i+2] = "0123456789ABCDEF"[b&15]
i += 3
}
} else if !unescaped[rn] {
buf[i] = '%'
buf[i+1] = "0123456789ABCDEF"[rn>>4]
buf[i+2] = "0123456789ABCDEF"[rn&15]
i += 3
} else {
buf[i] = byte(rn)
i++
}
}
return asciiString(string(buf))
}
func (r *Runtime) _decode(sv valueString, reservedSet *[256]bool) valueString {
s := sv.String()
hexCount := 0
for i := 0; i < len(s); {
switch s[i] {
case '%':
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
panic(r.newError(r.global.URIError, "Malformed URI"))
}
c := unhex(s[i+1])<<4 | unhex(s[i+2])
if !reservedSet[c] {
hexCount++
}
i += 3
default:
i++
}
}
if hexCount == 0 {
return sv
}
t := make([]byte, len(s)-hexCount*2)
j := 0
isUnicode := false
for i := 0; i < len(s); {
ch := s[i]
switch ch {
case '%':
c := unhex(s[i+1])<<4 | unhex(s[i+2])
if reservedSet[c] {
t[j] = s[i]
t[j+1] = s[i+1]
t[j+2] = s[i+2]
j += 3
} else {
t[j] = c
if c >= utf8.RuneSelf {
isUnicode = true
}
j++
}
i += 3
default:
if ch >= utf8.RuneSelf {
isUnicode = true
}
t[j] = ch
j++
i++
}
}
if !isUnicode {
return asciiString(t)
}
us := make([]rune, 0, len(s))
for len(t) > 0 {
rn, size := utf8.DecodeRune(t)
if rn == utf8.RuneError {
if size != 3 || t[0] != 0xef || t[1] != 0xbf || t[2] != 0xbd {
panic(r.newError(r.global.URIError, "Malformed URI"))
}
}
us = append(us, rn)
t = t[size:]
}
return unicodeString(utf16.Encode(us))
}
func ishex(c byte) bool {
switch {
case '0' <= c && c <= '9':
return true
case 'a' <= c && c <= 'f':
return true
case 'A' <= c && c <= 'F':
return true
}
return false
}
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}
func (r *Runtime) builtin_decodeURI(call FunctionCall) Value {
uriString := call.Argument(0).ToString()
return r._decode(uriString, &uriReservedHash)
}
func (r *Runtime) builtin_decodeURIComponent(call FunctionCall) Value {
uriString := call.Argument(0).ToString()
return r._decode(uriString, &emptyEscapeSet)
}
func (r *Runtime) builtin_encodeURI(call FunctionCall) Value {
uriString := call.Argument(0).ToString()
return r._encode(uriString, &uriReservedUnescapedHash)
}
func (r *Runtime) builtin_encodeURIComponent(call FunctionCall) Value {
uriString := call.Argument(0).ToString()
return r._encode(uriString, &uriUnescaped)
}
func (r *Runtime) initGlobalObject() {
o := r.globalObject.self
o._putProp("NaN", _NaN, false, false, false)
o._putProp("undefined", _undefined, false, false, false)
o._putProp("Infinity", _positiveInf, false, false, false)
o._putProp("isNaN", r.newNativeFunc(r.builtin_isNaN, nil, "isNaN", nil, 1), true, false, true)
o._putProp("parseInt", r.newNativeFunc(r.builtin_parseInt, nil, "parseInt", nil, 2), true, false, true)
o._putProp("parseFloat", r.newNativeFunc(r.builtin_parseFloat, nil, "parseFloat", nil, 1), true, false, true)
o._putProp("isFinite", r.newNativeFunc(r.builtin_isFinite, nil, "isFinite", nil, 1), true, false, true)
o._putProp("decodeURI", r.newNativeFunc(r.builtin_decodeURI, nil, "decodeURI", nil, 1), true, false, true)
o._putProp("decodeURIComponent", r.newNativeFunc(r.builtin_decodeURIComponent, nil, "decodeURIComponent", nil, 1), true, false, true)
o._putProp("encodeURI", r.newNativeFunc(r.builtin_encodeURI, nil, "encodeURI", nil, 1), true, false, true)
o._putProp("encodeURIComponent", r.newNativeFunc(r.builtin_encodeURIComponent, nil, "encodeURIComponent", nil, 1), true, false, true)
o._putProp("toString", r.newNativeFunc(func(FunctionCall) Value {
return stringGlobalObject
}, nil, "toString", nil, 0), false, false, false)
// TODO: Annex B
}
func digitVal(d byte) int {
var v byte
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
return 36
}
return int(v)
}
// ECMAScript compatible version of strconv.ParseInt
func parseInt(s string, base int) (Value, error) {
var n int64
var err error
var cutoff, maxVal int64
var sign bool
i := 0
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
switch s[0] {
case '-':
sign = true
s = s[1:]
case '+':
s = s[1:]
}
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
// Look for hex prefix.
if s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X') {
if base == 0 || base == 16 {
base = 16
s = s[2:]
}
}
switch {
case len(s) < 1:
err = strconv.ErrSyntax
goto Error
case 2 <= base && base <= 36:
// valid base; nothing to do
case base == 0:
// Look for hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
if len(s) < 3 {
err = strconv.ErrSyntax
goto Error
}
base = 16
s = s[2:]
default:
base = 10
}
default:
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}
// Cutoff is the smallest number such that cutoff*base > maxInt64.
// Use compile-time constants for common cases.
switch base {
case 10:
cutoff = math.MaxInt64/10 + 1
case 16:
cutoff = math.MaxInt64/16 + 1
default:
cutoff = math.MaxInt64/int64(base) + 1
}
maxVal = math.MaxInt64
for ; i < len(s); i++ {
if n >= cutoff {
// n*base overflows
return parseLargeInt(float64(n), s[i:], base, sign)
}
v := digitVal(s[i])
if v >= base {
break
}
n *= int64(base)
n1 := n + int64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
return parseLargeInt(float64(n)+float64(v), s[i+1:], base, sign)
}
n = n1
}
if i == 0 {
err = strconv.ErrSyntax
goto Error
}
if sign {
n = -n
}
return intToValue(n), nil
Error:
return _NaN, err
}
func parseLargeInt(n float64, s string, base int, sign bool) (Value, error) {
i := 0
b := float64(base)
for ; i < len(s); i++ {
v := digitVal(s[i])
if v >= base {
break
}
n = n*b + float64(v)
}
if sign {
n = -n
}
// We know it can't be represented as int, so use valueFloat instead of floatToValue
return valueFloat(n), nil
}
var (
uriUnescaped [256]bool
uriReserved [256]bool
uriReservedHash [256]bool
uriReservedUnescapedHash [256]bool
emptyEscapeSet [256]bool
)
func init() {
for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" {
uriUnescaped[c] = true
}
for _, c := range ";/?:@&=+$," {
uriReserved[c] = true
}
for i := 0; i < 256; i++ {
if uriUnescaped[i] || uriReserved[i] {
uriReservedUnescapedHash[i] = true
}
uriReservedHash[i] = uriReserved[i]
}
uriReservedUnescapedHash['#'] = true
uriReservedHash['#'] = true
}

520
vendor/github.com/dop251/goja/builtin_json.go generated vendored Normal file
View file

@ -0,0 +1,520 @@
package goja
import (
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"strings"
)
var hex = "0123456789abcdef"
func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
d := json.NewDecoder(bytes.NewBufferString(call.Argument(0).String()))
value, err := r.builtinJSON_decodeValue(d)
if err != nil {
panic(r.newError(r.global.SyntaxError, err.Error()))
}
if tok, err := d.Token(); err != io.EOF {
panic(r.newError(r.global.SyntaxError, "Unexpected token at the end: %v", tok))
}
var reviver func(FunctionCall) Value
if arg1 := call.Argument(1); arg1 != _undefined {
reviver, _ = arg1.ToObject(r).self.assertCallable()
}
if reviver != nil {
root := r.NewObject()
root.self.putStr("", value, false)
return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
}
return value
}
func (r *Runtime) builtinJSON_decodeToken(d *json.Decoder, tok json.Token) (Value, error) {
switch tok := tok.(type) {
case json.Delim:
switch tok {
case '{':
return r.builtinJSON_decodeObject(d)
case '[':
return r.builtinJSON_decodeArray(d)
}
case nil:
return _null, nil
case string:
return newStringValue(tok), nil
case float64:
return floatToValue(tok), nil
case bool:
if tok {
return valueTrue, nil
}
return valueFalse, nil
}
return nil, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
}
func (r *Runtime) builtinJSON_decodeValue(d *json.Decoder) (Value, error) {
tok, err := d.Token()
if err != nil {
return nil, err
}
return r.builtinJSON_decodeToken(d, tok)
}
func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
object := r.NewObject()
for {
key, end, err := r.builtinJSON_decodeObjectKey(d)
if err != nil {
return nil, err
}
if end {
break
}
value, err := r.builtinJSON_decodeValue(d)
if err != nil {
return nil, err
}
if key == "__proto__" {
descr := propertyDescr{
Value: value,
Writable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
Configurable: FLAG_TRUE,
}
object.self.defineOwnProperty(string__proto__, descr, false)
} else {
object.self.putStr(key, value, false)
}
}
return object, nil
}
func (r *Runtime) builtinJSON_decodeObjectKey(d *json.Decoder) (string, bool, error) {
tok, err := d.Token()
if err != nil {
return "", false, err
}
switch tok := tok.(type) {
case json.Delim:
if tok == '}' {
return "", true, nil
}
case string:
return tok, false, nil
}
return "", false, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
}
func (r *Runtime) builtinJSON_decodeArray(d *json.Decoder) (*Object, error) {
var arrayValue []Value
for {
tok, err := d.Token()
if err != nil {
return nil, err
}
if delim, ok := tok.(json.Delim); ok {
if delim == ']' {
break
}
}
value, err := r.builtinJSON_decodeToken(d, tok)
if err != nil {
return nil, err
}
arrayValue = append(arrayValue, value)
}
return r.newArrayValues(arrayValue), nil
}
func isArray(object *Object) bool {
switch object.self.className() {
case classArray:
return true
default:
return false
}
}
func (r *Runtime) builtinJSON_reviveWalk(reviver func(FunctionCall) Value, holder *Object, name Value) Value {
value := holder.self.get(name)
if value == nil {
value = _undefined
}
if object := value.(*Object); object != nil {
if isArray(object) {
length := object.self.getStr("length").ToInteger()
for index := int64(0); index < length; index++ {
name := intToValue(index)
value := r.builtinJSON_reviveWalk(reviver, object, name)
if value == _undefined {
object.self.delete(name, false)
} else {
object.self.put(name, value, false)
}
}
} else {
for item, f := object.self.enumerate(false, false)(); f != nil; item, f = f() {
value := r.builtinJSON_reviveWalk(reviver, object, name)
if value == _undefined {
object.self.deleteStr(item.name, false)
} else {
object.self.putStr(item.name, value, false)
}
}
}
}
return reviver(FunctionCall{
This: holder,
Arguments: []Value{name, value},
})
}
type _builtinJSON_stringifyContext struct {
r *Runtime
stack []*Object
propertyList []Value
replacerFunction func(FunctionCall) Value
gap, indent string
buf bytes.Buffer
}
func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
ctx := _builtinJSON_stringifyContext{
r: r,
}
replacer, _ := call.Argument(1).(*Object)
if replacer != nil {
if isArray(replacer) {
length := replacer.self.getStr("length").ToInteger()
seen := map[string]bool{}
propertyList := make([]Value, length)
length = 0
for index := range propertyList {
var name string
value := replacer.self.get(intToValue(int64(index)))
if s, ok := value.assertString(); ok {
name = s.String()
} else if _, ok := value.assertInt(); ok {
name = value.String()
} else if _, ok := value.assertFloat(); ok {
name = value.String()
} else if o, ok := value.(*Object); ok {
switch o.self.className() {
case classNumber, classString:
name = value.String()
}
}
if seen[name] {
continue
}
seen[name] = true
length += 1
propertyList[index] = newStringValue(name)
}
ctx.propertyList = propertyList[0:length]
} else if c, ok := replacer.self.assertCallable(); ok {
ctx.replacerFunction = c
}
}
if spaceValue := call.Argument(2); spaceValue != _undefined {
if o, ok := spaceValue.(*Object); ok {
switch o := o.self.(type) {
case *primitiveValueObject:
spaceValue = o.pValue
case *stringObject:
spaceValue = o.value
}
}
isNum := false
var num int64
num, isNum = spaceValue.assertInt()
if !isNum {
if f, ok := spaceValue.assertFloat(); ok {
num = int64(f)
isNum = true
}
}
if isNum {
if num > 0 {
if num > 10 {
num = 10
}
ctx.gap = strings.Repeat(" ", int(num))
}
} else {
if s, ok := spaceValue.assertString(); ok {
str := s.String()
if len(str) > 10 {
ctx.gap = str[:10]
} else {
ctx.gap = str
}
}
}
}
if ctx.do(call.Argument(0)) {
return newStringValue(ctx.buf.String())
}
return _undefined
}
func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
holder := ctx.r.NewObject()
holder.self.putStr("", v, false)
return ctx.str(stringEmpty, holder)
}
func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
value := holder.self.get(key)
if value == nil {
value = _undefined
}
if object, ok := value.(*Object); ok {
if toJSON, ok := object.self.getStr("toJSON").(*Object); ok {
if c, ok := toJSON.self.assertCallable(); ok {
value = c(FunctionCall{
This: value,
Arguments: []Value{key},
})
}
}
}
if ctx.replacerFunction != nil {
value = ctx.replacerFunction(FunctionCall{
This: holder,
Arguments: []Value{key, value},
})
}
if o, ok := value.(*Object); ok {
switch o1 := o.self.(type) {
case *primitiveValueObject:
value = o1.pValue
case *stringObject:
value = o1.value
case *objectGoReflect:
if o1.toJson != nil {
value = ctx.r.ToValue(o1.toJson())
} else if v, ok := o1.origValue.Interface().(json.Marshaler); ok {
b, err := v.MarshalJSON()
if err != nil {
panic(err)
}
ctx.buf.Write(b)
return true
} else {
switch o1.className() {
case classNumber:
value = o1.toPrimitiveNumber()
case classString:
value = o1.toPrimitiveString()
case classBoolean:
if o.ToInteger() != 0 {
value = valueTrue
} else {
value = valueFalse
}
}
}
}
}
switch value1 := value.(type) {
case valueBool:
if value1 {
ctx.buf.WriteString("true")
} else {
ctx.buf.WriteString("false")
}
case valueString:
ctx.quote(value1)
case valueInt:
ctx.buf.WriteString(value.String())
case valueFloat:
if !math.IsNaN(float64(value1)) && !math.IsInf(float64(value1), 0) {
ctx.buf.WriteString(value.String())
} else {
ctx.buf.WriteString("null")
}
case valueNull:
ctx.buf.WriteString("null")
case *Object:
for _, object := range ctx.stack {
if value1 == object {
ctx.r.typeErrorResult(true, "Converting circular structure to JSON")
}
}
ctx.stack = append(ctx.stack, value1)
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
if _, ok := value1.self.assertCallable(); !ok {
if isArray(value1) {
ctx.ja(value1)
} else {
ctx.jo(value1)
}
} else {
return false
}
default:
return false
}
return true
}
func (ctx *_builtinJSON_stringifyContext) ja(array *Object) {
var stepback string
if ctx.gap != "" {
stepback = ctx.indent
ctx.indent += ctx.gap
}
length := array.self.getStr("length").ToInteger()
if length == 0 {
ctx.buf.WriteString("[]")
return
}
ctx.buf.WriteByte('[')
var separator string
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(ctx.indent)
separator = ",\n" + ctx.indent
} else {
separator = ","
}
for i := int64(0); i < length; i++ {
if !ctx.str(intToValue(i), array) {
ctx.buf.WriteString("null")
}
if i < length-1 {
ctx.buf.WriteString(separator)
}
}
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(stepback)
ctx.indent = stepback
}
ctx.buf.WriteByte(']')
}
func (ctx *_builtinJSON_stringifyContext) jo(object *Object) {
var stepback string
if ctx.gap != "" {
stepback = ctx.indent
ctx.indent += ctx.gap
}
ctx.buf.WriteByte('{')
mark := ctx.buf.Len()
var separator string
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(ctx.indent)
separator = ",\n" + ctx.indent
} else {
separator = ","
}
var props []Value
if ctx.propertyList == nil {
for item, f := object.self.enumerate(false, false)(); f != nil; item, f = f() {
props = append(props, newStringValue(item.name))
}
} else {
props = ctx.propertyList
}
empty := true
for _, name := range props {
off := ctx.buf.Len()
if !empty {
ctx.buf.WriteString(separator)
}
ctx.quote(name.ToString())
if ctx.gap != "" {
ctx.buf.WriteString(": ")
} else {
ctx.buf.WriteByte(':')
}
if ctx.str(name, object) {
if empty {
empty = false
}
} else {
ctx.buf.Truncate(off)
}
}
if empty {
ctx.buf.Truncate(mark)
} else {
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(stepback)
ctx.indent = stepback
}
}
ctx.buf.WriteByte('}')
}
func (ctx *_builtinJSON_stringifyContext) quote(str valueString) {
ctx.buf.WriteByte('"')
reader := str.reader(0)
for {
r, _, err := reader.ReadRune()
if err != nil {
break
}
switch r {
case '"', '\\':
ctx.buf.WriteByte('\\')
ctx.buf.WriteByte(byte(r))
case 0x08:
ctx.buf.WriteString(`\b`)
case 0x09:
ctx.buf.WriteString(`\t`)
case 0x0A:
ctx.buf.WriteString(`\n`)
case 0x0C:
ctx.buf.WriteString(`\f`)
case 0x0D:
ctx.buf.WriteString(`\r`)
default:
if r < 0x20 {
ctx.buf.WriteString(`\u00`)
ctx.buf.WriteByte(hex[r>>4])
ctx.buf.WriteByte(hex[r&0xF])
} else {
ctx.buf.WriteRune(r)
}
}
}
ctx.buf.WriteByte('"')
}
func (r *Runtime) initJSON() {
JSON := r.newBaseObject(r.global.ObjectPrototype, "JSON")
JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, nil, "parse", nil, 2), true, false, true)
JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, nil, "stringify", nil, 3), true, false, true)
r.addToGlobal("JSON", JSON.val)
}

192
vendor/github.com/dop251/goja/builtin_math.go generated vendored Normal file
View file

@ -0,0 +1,192 @@
package goja
import (
"math"
)
func (r *Runtime) math_abs(call FunctionCall) Value {
return floatToValue(math.Abs(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_acos(call FunctionCall) Value {
return floatToValue(math.Acos(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_asin(call FunctionCall) Value {
return floatToValue(math.Asin(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_atan(call FunctionCall) Value {
return floatToValue(math.Atan(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_atan2(call FunctionCall) Value {
y := call.Argument(0).ToFloat()
x := call.Argument(1).ToFloat()
return floatToValue(math.Atan2(y, x))
}
func (r *Runtime) math_ceil(call FunctionCall) Value {
return floatToValue(math.Ceil(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_cos(call FunctionCall) Value {
return floatToValue(math.Cos(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_exp(call FunctionCall) Value {
return floatToValue(math.Exp(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_floor(call FunctionCall) Value {
return floatToValue(math.Floor(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_log(call FunctionCall) Value {
return floatToValue(math.Log(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_max(call FunctionCall) Value {
if len(call.Arguments) == 0 {
return _negativeInf
}
result := call.Arguments[0].ToFloat()
if math.IsNaN(result) {
return _NaN
}
for _, arg := range call.Arguments[1:] {
f := arg.ToFloat()
if math.IsNaN(f) {
return _NaN
}
result = math.Max(result, f)
}
return floatToValue(result)
}
func (r *Runtime) math_min(call FunctionCall) Value {
if len(call.Arguments) == 0 {
return _positiveInf
}
result := call.Arguments[0].ToFloat()
if math.IsNaN(result) {
return _NaN
}
for _, arg := range call.Arguments[1:] {
f := arg.ToFloat()
if math.IsNaN(f) {
return _NaN
}
result = math.Min(result, f)
}
return floatToValue(result)
}
func (r *Runtime) math_pow(call FunctionCall) Value {
x := call.Argument(0)
y := call.Argument(1)
if x, ok := x.assertInt(); ok {
if y, ok := y.assertInt(); ok && y >= 0 && y < 64 {
if y == 0 {
return intToValue(1)
}
if x == 0 {
return intToValue(0)
}
ip := ipow(x, y)
if ip != 0 {
return intToValue(ip)
}
}
}
return floatToValue(math.Pow(x.ToFloat(), y.ToFloat()))
}
func (r *Runtime) math_random(call FunctionCall) Value {
return floatToValue(r.rand())
}
func (r *Runtime) math_round(call FunctionCall) Value {
f := call.Argument(0).ToFloat()
if math.IsNaN(f) {
return _NaN
}
if f == 0 && math.Signbit(f) {
return _negativeZero
}
t := math.Trunc(f)
if f >= 0 {
if f-t >= 0.5 {
return floatToValue(t + 1)
}
} else {
if t-f > 0.5 {
return floatToValue(t - 1)
}
}
return floatToValue(t)
}
func (r *Runtime) math_sin(call FunctionCall) Value {
return floatToValue(math.Sin(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_sqrt(call FunctionCall) Value {
return floatToValue(math.Sqrt(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_tan(call FunctionCall) Value {
return floatToValue(math.Tan(call.Argument(0).ToFloat()))
}
func (r *Runtime) createMath(val *Object) objectImpl {
m := &baseObject{
class: "Math",
val: val,
extensible: true,
prototype: r.global.ObjectPrototype,
}
m.init()
m._putProp("E", valueFloat(math.E), false, false, false)
m._putProp("LN10", valueFloat(math.Ln10), false, false, false)
m._putProp("LN2", valueFloat(math.Ln2), false, false, false)
m._putProp("LOG2E", valueFloat(math.Log2E), false, false, false)
m._putProp("LOG10E", valueFloat(math.Log10E), false, false, false)
m._putProp("PI", valueFloat(math.Pi), false, false, false)
m._putProp("SQRT1_2", valueFloat(sqrt1_2), false, false, false)
m._putProp("SQRT2", valueFloat(math.Sqrt2), false, false, false)
m._putProp("abs", r.newNativeFunc(r.math_abs, nil, "abs", nil, 1), true, false, true)
m._putProp("acos", r.newNativeFunc(r.math_acos, nil, "acos", nil, 1), true, false, true)
m._putProp("asin", r.newNativeFunc(r.math_asin, nil, "asin", nil, 1), true, false, true)
m._putProp("atan", r.newNativeFunc(r.math_atan, nil, "atan", nil, 1), true, false, true)
m._putProp("atan2", r.newNativeFunc(r.math_atan2, nil, "atan2", nil, 2), true, false, true)
m._putProp("ceil", r.newNativeFunc(r.math_ceil, nil, "ceil", nil, 1), true, false, true)
m._putProp("cos", r.newNativeFunc(r.math_cos, nil, "cos", nil, 1), true, false, true)
m._putProp("exp", r.newNativeFunc(r.math_exp, nil, "exp", nil, 1), true, false, true)
m._putProp("floor", r.newNativeFunc(r.math_floor, nil, "floor", nil, 1), true, false, true)
m._putProp("log", r.newNativeFunc(r.math_log, nil, "log", nil, 1), true, false, true)
m._putProp("max", r.newNativeFunc(r.math_max, nil, "max", nil, 2), true, false, true)
m._putProp("min", r.newNativeFunc(r.math_min, nil, "min", nil, 2), true, false, true)
m._putProp("pow", r.newNativeFunc(r.math_pow, nil, "pow", nil, 2), true, false, true)
m._putProp("random", r.newNativeFunc(r.math_random, nil, "random", nil, 0), true, false, true)
m._putProp("round", r.newNativeFunc(r.math_round, nil, "round", nil, 1), true, false, true)
m._putProp("sin", r.newNativeFunc(r.math_sin, nil, "sin", nil, 1), true, false, true)
m._putProp("sqrt", r.newNativeFunc(r.math_sqrt, nil, "sqrt", nil, 1), true, false, true)
m._putProp("tan", r.newNativeFunc(r.math_tan, nil, "tan", nil, 1), true, false, true)
return m
}
func (r *Runtime) initMath() {
r.addToGlobal("Math", r.newLazyObject(r.createMath))
}

154
vendor/github.com/dop251/goja/builtin_number.go generated vendored Normal file
View file

@ -0,0 +1,154 @@
package goja
import (
"math"
"strconv"
)
func (r *Runtime) numberproto_valueOf(call FunctionCall) Value {
this := call.This
if !isNumber(this) {
r.typeErrorResult(true, "Value is not a number")
}
if _, ok := this.assertInt(); ok {
return this
}
if _, ok := this.assertFloat(); ok {
return this
}
if obj, ok := this.(*Object); ok {
if v, ok := obj.self.(*primitiveValueObject); ok {
return v.pValue
}
}
r.typeErrorResult(true, "Number.prototype.valueOf is not generic")
return nil
}
func isNumber(v Value) bool {
switch t := v.(type) {
case valueFloat, valueInt:
return true
case *Object:
switch t := t.self.(type) {
case *primitiveValueObject:
return isNumber(t.pValue)
}
}
return false
}
func (r *Runtime) numberproto_toString(call FunctionCall) Value {
if !isNumber(call.This) {
r.typeErrorResult(true, "Value is not a number")
}
var radix int
if arg := call.Argument(0); arg != _undefined {
radix = int(arg.ToInteger())
} else {
radix = 10
}
if radix < 2 || radix > 36 {
panic(r.newError(r.global.RangeError, "toString() radix argument must be between 2 and 36"))
}
num := call.This.ToFloat()
if math.IsNaN(num) {
return stringNaN
}
if math.IsInf(num, 1) {
return stringInfinity
}
if math.IsInf(num, -1) {
return stringNegInfinity
}
if radix == 10 {
var fmt byte
if math.Abs(num) >= 1e21 {
fmt = 'e'
} else {
fmt = 'f'
}
return asciiString(strconv.FormatFloat(num, fmt, -1, 64))
}
return asciiString(dtobasestr(num, radix))
}
func (r *Runtime) numberproto_toFixed(call FunctionCall) Value {
prec := call.Argument(0).ToInteger()
if prec < 0 || prec > 20 {
panic(r.newError(r.global.RangeError, "toFixed() precision must be between 0 and 20"))
}
num := call.This.ToFloat()
if math.IsNaN(num) {
return stringNaN
}
if math.Abs(num) >= 1e21 {
return asciiString(strconv.FormatFloat(num, 'g', -1, 64))
}
return asciiString(strconv.FormatFloat(num, 'f', int(prec), 64))
}
func (r *Runtime) numberproto_toExponential(call FunctionCall) Value {
prec := call.Argument(0).ToInteger()
if prec < 0 || prec > 20 {
panic(r.newError(r.global.RangeError, "toExponential() precision must be between 0 and 20"))
}
num := call.This.ToFloat()
if math.IsNaN(num) {
return stringNaN
}
if math.Abs(num) >= 1e21 {
return asciiString(strconv.FormatFloat(num, 'g', -1, 64))
}
return asciiString(strconv.FormatFloat(num, 'e', int(prec), 64))
}
func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value {
prec := call.Argument(0).ToInteger()
if prec < 0 || prec > 20 {
panic(r.newError(r.global.RangeError, "toPrecision() precision must be between 0 and 20"))
}
num := call.This.ToFloat()
if math.IsNaN(num) {
return stringNaN
}
if math.Abs(num) >= 1e21 {
return asciiString(strconv.FormatFloat(num, 'g', -1, 64))
}
return asciiString(strconv.FormatFloat(num, 'g', int(prec), 64))
}
func (r *Runtime) initNumber() {
r.global.NumberPrototype = r.newPrimitiveObject(valueInt(0), r.global.ObjectPrototype, classNumber)
o := r.global.NumberPrototype.self
o._putProp("valueOf", r.newNativeFunc(r.numberproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
o._putProp("toString", r.newNativeFunc(r.numberproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("toLocaleString", r.newNativeFunc(r.numberproto_toString, nil, "toLocaleString", nil, 0), true, false, true)
o._putProp("toFixed", r.newNativeFunc(r.numberproto_toFixed, nil, "toFixed", nil, 1), true, false, true)
o._putProp("toExponential", r.newNativeFunc(r.numberproto_toExponential, nil, "toExponential", nil, 1), true, false, true)
o._putProp("toPrecision", r.newNativeFunc(r.numberproto_toPrecision, nil, "toPrecision", nil, 1), true, false, true)
r.global.Number = r.newNativeFunc(r.builtin_Number, r.builtin_newNumber, "Number", r.global.NumberPrototype, 1)
o = r.global.Number.self
o._putProp("MAX_VALUE", valueFloat(math.MaxFloat64), false, false, false)
o._putProp("MIN_VALUE", valueFloat(math.SmallestNonzeroFloat64), false, false, false)
o._putProp("NaN", _NaN, false, false, false)
o._putProp("NEGATIVE_INFINITY", _negativeInf, false, false, false)
o._putProp("POSITIVE_INFINITY", _positiveInf, false, false, false)
o._putProp("EPSILON", _epsilon, false, false, false)
r.addToGlobal("Number", r.global.Number)
}

412
vendor/github.com/dop251/goja/builtin_object.go generated vendored Normal file
View file

@ -0,0 +1,412 @@
package goja
import (
"fmt"
)
func (r *Runtime) builtin_Object(args []Value, proto *Object) *Object {
if len(args) > 0 {
arg := args[0]
if arg != _undefined && arg != _null {
return arg.ToObject(r)
}
}
return r.NewObject()
}
func (r *Runtime) object_getPrototypeOf(call FunctionCall) Value {
o := call.Argument(0).ToObject(r)
p := o.self.proto()
if p == nil {
return _null
}
return p
}
func (r *Runtime) object_getOwnPropertyDescriptor(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
propName := call.Argument(1).String()
desc := obj.self.getOwnProp(propName)
if desc == nil {
return _undefined
}
var writable, configurable, enumerable, accessor bool
var get, set *Object
var value Value
if v, ok := desc.(*valueProperty); ok {
writable = v.writable
configurable = v.configurable
enumerable = v.enumerable
accessor = v.accessor
value = v.value
get = v.getterFunc
set = v.setterFunc
} else {
writable = true
configurable = true
enumerable = true
value = desc
}
ret := r.NewObject()
o := ret.self
if !accessor {
o.putStr("value", value, false)
o.putStr("writable", r.toBoolean(writable), false)
} else {
if get != nil {
o.putStr("get", get, false)
} else {
o.putStr("get", _undefined, false)
}
if set != nil {
o.putStr("set", set, false)
} else {
o.putStr("set", _undefined, false)
}
}
o.putStr("enumerable", r.toBoolean(enumerable), false)
o.putStr("configurable", r.toBoolean(configurable), false)
return ret
}
func (r *Runtime) object_getOwnPropertyNames(call FunctionCall) Value {
// ES6
obj := call.Argument(0).ToObject(r)
// obj := r.toObject(call.Argument(0))
var values []Value
for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
values = append(values, newStringValue(item.name))
}
return r.newArrayValues(values)
}
func (r *Runtime) toPropertyDescr(v Value) (ret propertyDescr) {
if o, ok := v.(*Object); ok {
descr := o.self
ret.Value = descr.getStr("value")
if p := descr.getStr("writable"); p != nil {
ret.Writable = ToFlag(p.ToBoolean())
}
if p := descr.getStr("enumerable"); p != nil {
ret.Enumerable = ToFlag(p.ToBoolean())
}
if p := descr.getStr("configurable"); p != nil {
ret.Configurable = ToFlag(p.ToBoolean())
}
ret.Getter = descr.getStr("get")
ret.Setter = descr.getStr("set")
if ret.Getter != nil && ret.Getter != _undefined {
if _, ok := r.toObject(ret.Getter).self.assertCallable(); !ok {
r.typeErrorResult(true, "getter must be a function")
}
}
if ret.Setter != nil && ret.Setter != _undefined {
if _, ok := r.toObject(ret.Setter).self.assertCallable(); !ok {
r.typeErrorResult(true, "setter must be a function")
}
}
if (ret.Getter != nil || ret.Setter != nil) && (ret.Value != nil || ret.Writable != FLAG_NOT_SET) {
r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
return
}
} else {
r.typeErrorResult(true, "Property description must be an object: %s", v.String())
}
return
}
func (r *Runtime) _defineProperties(o *Object, p Value) {
type propItem struct {
name string
prop propertyDescr
}
props := p.ToObject(r)
var list []propItem
for item, f := props.self.enumerate(false, false)(); f != nil; item, f = f() {
list = append(list, propItem{
name: item.name,
prop: r.toPropertyDescr(props.self.getStr(item.name)),
})
}
for _, prop := range list {
o.self.defineOwnProperty(newStringValue(prop.name), prop.prop, true)
}
}
func (r *Runtime) object_create(call FunctionCall) Value {
var proto *Object
if arg := call.Argument(0); arg != _null {
if o, ok := arg.(*Object); ok {
proto = o
} else {
r.typeErrorResult(true, "Object prototype may only be an Object or null: %s", arg.String())
}
}
o := r.newBaseObject(proto, classObject).val
if props := call.Argument(1); props != _undefined {
r._defineProperties(o, props)
}
return o
}
func (r *Runtime) object_defineProperty(call FunctionCall) (ret Value) {
if obj, ok := call.Argument(0).(*Object); ok {
descr := r.toPropertyDescr(call.Argument(2))
obj.self.defineOwnProperty(call.Argument(1), descr, true)
ret = call.Argument(0)
} else {
r.typeErrorResult(true, "Object.defineProperty called on non-object")
}
return
}
func (r *Runtime) object_defineProperties(call FunctionCall) Value {
obj := r.toObject(call.Argument(0))
r._defineProperties(obj, call.Argument(1))
return obj
}
func (r *Runtime) object_seal(call FunctionCall) Value {
// ES6
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
descr := propertyDescr{
Writable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
Configurable: FLAG_FALSE,
}
for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
v := obj.self.getOwnProp(item.name)
if prop, ok := v.(*valueProperty); ok {
if !prop.configurable {
continue
}
prop.configurable = false
} else {
descr.Value = v
obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
//obj.self._putProp(item.name, v, true, true, false)
}
}
obj.self.preventExtensions()
return obj
}
return arg
}
func (r *Runtime) object_freeze(call FunctionCall) Value {
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
descr := propertyDescr{
Writable: FLAG_FALSE,
Enumerable: FLAG_TRUE,
Configurable: FLAG_FALSE,
}
for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
v := obj.self.getOwnProp(item.name)
if prop, ok := v.(*valueProperty); ok {
prop.configurable = false
if prop.value != nil {
prop.writable = false
}
} else {
descr.Value = v
obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
}
}
obj.self.preventExtensions()
return obj
} else {
// ES6 behavior
return arg
}
}
func (r *Runtime) object_preventExtensions(call FunctionCall) (ret Value) {
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
obj.self.preventExtensions()
return obj
}
// ES6
//r.typeErrorResult(true, "Object.preventExtensions called on non-object")
//panic("Unreachable")
return arg
}
func (r *Runtime) object_isSealed(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueFalse
}
for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
prop := obj.self.getOwnProp(item.name)
if prop, ok := prop.(*valueProperty); ok {
if prop.configurable {
return valueFalse
}
} else {
return valueFalse
}
}
} else {
// ES6
//r.typeErrorResult(true, "Object.isSealed called on non-object")
return valueTrue
}
return valueTrue
}
func (r *Runtime) object_isFrozen(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueFalse
}
for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
prop := obj.self.getOwnProp(item.name)
if prop, ok := prop.(*valueProperty); ok {
if prop.configurable || prop.value != nil && prop.writable {
return valueFalse
}
} else {
return valueFalse
}
}
} else {
// ES6
//r.typeErrorResult(true, "Object.isFrozen called on non-object")
return valueTrue
}
return valueTrue
}
func (r *Runtime) object_isExtensible(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueTrue
}
return valueFalse
} else {
// ES6
//r.typeErrorResult(true, "Object.isExtensible called on non-object")
return valueFalse
}
}
func (r *Runtime) object_keys(call FunctionCall) Value {
// ES6
obj := call.Argument(0).ToObject(r)
//if obj, ok := call.Argument(0).(*valueObject); ok {
var keys []Value
for item, f := obj.self.enumerate(false, false)(); f != nil; item, f = f() {
keys = append(keys, newStringValue(item.name))
}
return r.newArrayValues(keys)
//} else {
// r.typeErrorResult(true, "Object.keys called on non-object")
//}
//return nil
}
func (r *Runtime) objectproto_hasOwnProperty(call FunctionCall) Value {
p := call.Argument(0).String()
o := call.This.ToObject(r)
if o.self.hasOwnPropertyStr(p) {
return valueTrue
} else {
return valueFalse
}
}
func (r *Runtime) objectproto_isPrototypeOf(call FunctionCall) Value {
if v, ok := call.Argument(0).(*Object); ok {
o := call.This.ToObject(r)
for {
v = v.self.proto()
if v == nil {
break
}
if v == o {
return valueTrue
}
}
}
return valueFalse
}
func (r *Runtime) objectproto_propertyIsEnumerable(call FunctionCall) Value {
p := call.Argument(0).ToString()
o := call.This.ToObject(r)
pv := o.self.getOwnProp(p.String())
if pv == nil {
return valueFalse
}
if prop, ok := pv.(*valueProperty); ok {
if !prop.enumerable {
return valueFalse
}
}
return valueTrue
}
func (r *Runtime) objectproto_toString(call FunctionCall) Value {
switch o := call.This.(type) {
case valueNull:
return stringObjectNull
case valueUndefined:
return stringObjectUndefined
case *Object:
return newStringValue(fmt.Sprintf("[object %s]", o.self.className()))
default:
obj := call.This.ToObject(r)
return newStringValue(fmt.Sprintf("[object %s]", obj.self.className()))
}
}
func (r *Runtime) objectproto_toLocaleString(call FunctionCall) Value {
return call.This.ToObject(r).ToString()
}
func (r *Runtime) objectproto_valueOf(call FunctionCall) Value {
return call.This.ToObject(r)
}
func (r *Runtime) initObject() {
o := r.global.ObjectPrototype.self
o._putProp("toString", r.newNativeFunc(r.objectproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("toLocaleString", r.newNativeFunc(r.objectproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
o._putProp("valueOf", r.newNativeFunc(r.objectproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
o._putProp("hasOwnProperty", r.newNativeFunc(r.objectproto_hasOwnProperty, nil, "hasOwnProperty", nil, 1), true, false, true)
o._putProp("isPrototypeOf", r.newNativeFunc(r.objectproto_isPrototypeOf, nil, "isPrototypeOf", nil, 1), true, false, true)
o._putProp("propertyIsEnumerable", r.newNativeFunc(r.objectproto_propertyIsEnumerable, nil, "propertyIsEnumerable", nil, 1), true, false, true)
r.global.Object = r.newNativeFuncConstruct(r.builtin_Object, classObject, r.global.ObjectPrototype, 1)
o = r.global.Object.self
o._putProp("defineProperty", r.newNativeFunc(r.object_defineProperty, nil, "defineProperty", nil, 3), true, false, true)
o._putProp("defineProperties", r.newNativeFunc(r.object_defineProperties, nil, "defineProperties", nil, 2), true, false, true)
o._putProp("getOwnPropertyDescriptor", r.newNativeFunc(r.object_getOwnPropertyDescriptor, nil, "getOwnPropertyDescriptor", nil, 2), true, false, true)
o._putProp("getPrototypeOf", r.newNativeFunc(r.object_getPrototypeOf, nil, "getPrototypeOf", nil, 1), true, false, true)
o._putProp("getOwnPropertyNames", r.newNativeFunc(r.object_getOwnPropertyNames, nil, "getOwnPropertyNames", nil, 1), true, false, true)
o._putProp("create", r.newNativeFunc(r.object_create, nil, "create", nil, 2), true, false, true)
o._putProp("seal", r.newNativeFunc(r.object_seal, nil, "seal", nil, 1), true, false, true)
o._putProp("freeze", r.newNativeFunc(r.object_freeze, nil, "freeze", nil, 1), true, false, true)
o._putProp("preventExtensions", r.newNativeFunc(r.object_preventExtensions, nil, "preventExtensions", nil, 1), true, false, true)
o._putProp("isSealed", r.newNativeFunc(r.object_isSealed, nil, "isSealed", nil, 1), true, false, true)
o._putProp("isFrozen", r.newNativeFunc(r.object_isFrozen, nil, "isFrozen", nil, 1), true, false, true)
o._putProp("isExtensible", r.newNativeFunc(r.object_isExtensible, nil, "isExtensible", nil, 1), true, false, true)
o._putProp("keys", r.newNativeFunc(r.object_keys, nil, "keys", nil, 1), true, false, true)
r.addToGlobal("Object", r.global.Object)
}

272
vendor/github.com/dop251/goja/builtin_regexp.go generated vendored Normal file
View file

@ -0,0 +1,272 @@
package goja
import (
"fmt"
"github.com/dlclark/regexp2"
"github.com/dop251/goja/parser"
"regexp"
)
func (r *Runtime) newRegexpObject(proto *Object) *regexpObject {
v := &Object{runtime: r}
o := &regexpObject{}
o.class = classRegExp
o.val = v
o.extensible = true
v.self = o
o.prototype = proto
o.init()
return o
}
func (r *Runtime) newRegExpp(pattern regexpPattern, patternStr valueString, global, ignoreCase, multiline bool, proto *Object) *Object {
o := r.newRegexpObject(proto)
o.pattern = pattern
o.source = patternStr
o.global = global
o.ignoreCase = ignoreCase
o.multiline = multiline
return o.val
}
func compileRegexp(patternStr, flags string) (p regexpPattern, global, ignoreCase, multiline bool, err error) {
if flags != "" {
invalidFlags := func() {
err = fmt.Errorf("Invalid flags supplied to RegExp constructor '%s'", flags)
}
for _, chr := range flags {
switch chr {
case 'g':
if global {
invalidFlags()
return
}
global = true
case 'm':
if multiline {
invalidFlags()
return
}
multiline = true
case 'i':
if ignoreCase {
invalidFlags()
return
}
ignoreCase = true
default:
invalidFlags()
return
}
}
}
re2Str, err1 := parser.TransformRegExp(patternStr)
if /*false &&*/ err1 == nil {
re2flags := ""
if multiline {
re2flags += "m"
}
if ignoreCase {
re2flags += "i"
}
if len(re2flags) > 0 {
re2Str = fmt.Sprintf("(?%s:%s)", re2flags, re2Str)
}
pattern, err1 := regexp.Compile(re2Str)
if err1 != nil {
err = fmt.Errorf("Invalid regular expression (re2): %s (%v)", re2Str, err1)
return
}
p = (*regexpWrapper)(pattern)
} else {
var opts regexp2.RegexOptions = regexp2.ECMAScript
if multiline {
opts |= regexp2.Multiline
}
if ignoreCase {
opts |= regexp2.IgnoreCase
}
regexp2Pattern, err1 := regexp2.Compile(patternStr, opts)
if err1 != nil {
err = fmt.Errorf("Invalid regular expression (regexp2): %s (%v)", patternStr, err1)
return
}
p = (*regexp2Wrapper)(regexp2Pattern)
}
return
}
func (r *Runtime) newRegExp(patternStr valueString, flags string, proto *Object) *Object {
pattern, global, ignoreCase, multiline, err := compileRegexp(patternStr.String(), flags)
if err != nil {
panic(r.newSyntaxError(err.Error(), -1))
}
return r.newRegExpp(pattern, patternStr, global, ignoreCase, multiline, proto)
}
func (r *Runtime) builtin_newRegExp(args []Value) *Object {
var pattern valueString
var flags string
if len(args) > 0 {
if obj, ok := args[0].(*Object); ok {
if regexp, ok := obj.self.(*regexpObject); ok {
if len(args) < 2 || args[1] == _undefined {
return regexp.clone()
} else {
return r.newRegExp(regexp.source, args[1].String(), r.global.RegExpPrototype)
}
}
}
if args[0] != _undefined {
pattern = args[0].ToString()
}
}
if len(args) > 1 {
if a := args[1]; a != _undefined {
flags = a.String()
}
}
if pattern == nil {
pattern = stringEmpty
}
return r.newRegExp(pattern, flags, r.global.RegExpPrototype)
}
func (r *Runtime) builtin_RegExp(call FunctionCall) Value {
flags := call.Argument(1)
if flags == _undefined {
if obj, ok := call.Argument(0).(*Object); ok {
if _, ok := obj.self.(*regexpObject); ok {
return call.Arguments[0]
}
}
}
return r.builtin_newRegExp(call.Arguments)
}
func (r *Runtime) regexpproto_exec(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
return this.exec(call.Argument(0).ToString())
} else {
r.typeErrorResult(true, "Method RegExp.prototype.exec called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) regexpproto_test(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
if this.test(call.Argument(0).ToString()) {
return valueTrue
} else {
return valueFalse
}
} else {
r.typeErrorResult(true, "Method RegExp.prototype.test called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) regexpproto_toString(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
var g, i, m string
if this.global {
g = "g"
}
if this.ignoreCase {
i = "i"
}
if this.multiline {
m = "m"
}
return newStringValue(fmt.Sprintf("/%s/%s%s%s", this.source.String(), g, i, m))
} else {
r.typeErrorResult(true, "Method RegExp.prototype.toString called on incompatible receiver %s", call.This)
return nil
}
}
func (r *Runtime) regexpproto_getSource(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
return this.source
} else {
r.typeErrorResult(true, "Method RegExp.prototype.source getter called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) regexpproto_getGlobal(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
if this.global {
return valueTrue
} else {
return valueFalse
}
} else {
r.typeErrorResult(true, "Method RegExp.prototype.global getter called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) regexpproto_getMultiline(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
if this.multiline {
return valueTrue
} else {
return valueFalse
}
} else {
r.typeErrorResult(true, "Method RegExp.prototype.multiline getter called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) regexpproto_getIgnoreCase(call FunctionCall) Value {
if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
if this.ignoreCase {
return valueTrue
} else {
return valueFalse
}
} else {
r.typeErrorResult(true, "Method RegExp.prototype.ignoreCase getter called on incompatible receiver %s", call.This.ToString())
return nil
}
}
func (r *Runtime) initRegExp() {
r.global.RegExpPrototype = r.NewObject()
o := r.global.RegExpPrototype.self
o._putProp("exec", r.newNativeFunc(r.regexpproto_exec, nil, "exec", nil, 1), true, false, true)
o._putProp("test", r.newNativeFunc(r.regexpproto_test, nil, "test", nil, 1), true, false, true)
o._putProp("toString", r.newNativeFunc(r.regexpproto_toString, nil, "toString", nil, 0), true, false, true)
o.putStr("source", &valueProperty{
configurable: true,
getterFunc: r.newNativeFunc(r.regexpproto_getSource, nil, "get source", nil, 0),
accessor: true,
}, false)
o.putStr("global", &valueProperty{
configurable: true,
getterFunc: r.newNativeFunc(r.regexpproto_getGlobal, nil, "get global", nil, 0),
accessor: true,
}, false)
o.putStr("multiline", &valueProperty{
configurable: true,
getterFunc: r.newNativeFunc(r.regexpproto_getMultiline, nil, "get multiline", nil, 0),
accessor: true,
}, false)
o.putStr("ignoreCase", &valueProperty{
configurable: true,
getterFunc: r.newNativeFunc(r.regexpproto_getIgnoreCase, nil, "get ignoreCase", nil, 0),
accessor: true,
}, false)
r.global.RegExp = r.newNativeFunc(r.builtin_RegExp, r.builtin_newRegExp, "RegExp", r.global.RegExpPrototype, 2)
r.addToGlobal("RegExp", r.global.RegExp)
}

692
vendor/github.com/dop251/goja/builtin_string.go generated vendored Normal file
View file

@ -0,0 +1,692 @@
package goja
import (
"bytes"
"github.com/dop251/goja/parser"
"golang.org/x/text/collate"
"golang.org/x/text/language"
"golang.org/x/text/unicode/norm"
"math"
"strings"
"unicode/utf8"
)
func (r *Runtime) collator() *collate.Collator {
collator := r._collator
if collator == nil {
collator = collate.New(language.Und)
r._collator = collator
}
return collator
}
func (r *Runtime) builtin_String(call FunctionCall) Value {
if len(call.Arguments) > 0 {
arg := call.Arguments[0]
if _, ok := arg.assertString(); ok {
return arg
}
return arg.ToString()
} else {
return newStringValue("")
}
}
func (r *Runtime) _newString(s valueString) *Object {
v := &Object{runtime: r}
o := &stringObject{}
o.class = classString
o.val = v
o.extensible = true
v.self = o
o.prototype = r.global.StringPrototype
if s != nil {
o.value = s
}
o.init()
return v
}
func (r *Runtime) builtin_newString(args []Value) *Object {
var s valueString
if len(args) > 0 {
s = args[0].ToString()
} else {
s = stringEmpty
}
return r._newString(s)
}
func searchSubstringUTF8(str, search string) (ret [][]int) {
searchPos := 0
l := len(str)
if searchPos < l {
p := strings.Index(str[searchPos:], search)
if p != -1 {
p += searchPos
searchPos = p + len(search)
ret = append(ret, []int{p, searchPos})
}
}
return
}
func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value {
if str, ok := this.assertString(); ok {
return str
}
if obj, ok := this.(*Object); ok {
if strObj, ok := obj.self.(*stringObject); ok {
return strObj.value
}
}
r.typeErrorResult(true, "String.prototype.%s is called on incompatible receiver", funcName)
return nil
}
func (r *Runtime) stringproto_toString(call FunctionCall) Value {
return r.stringproto_toStringValueOf(call.This, "toString")
}
func (r *Runtime) stringproto_valueOf(call FunctionCall) Value {
return r.stringproto_toStringValueOf(call.This, "valueOf")
}
func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
b := make([]byte, len(call.Arguments))
for i, arg := range call.Arguments {
chr := toUInt16(arg)
if chr >= utf8.RuneSelf {
bb := make([]uint16, len(call.Arguments))
for j := 0; j < i; j++ {
bb[j] = uint16(b[j])
}
bb[i] = chr
i++
for j, arg := range call.Arguments[i:] {
bb[i+j] = toUInt16(arg)
}
return unicodeString(bb)
}
b[i] = byte(chr)
}
return asciiString(b)
}
func (r *Runtime) stringproto_charAt(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
pos := call.Argument(0).ToInteger()
if pos < 0 || pos >= s.length() {
return stringEmpty
}
return newStringValue(string(s.charAt(pos)))
}
func (r *Runtime) stringproto_charCodeAt(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
pos := call.Argument(0).ToInteger()
if pos < 0 || pos >= s.length() {
return _NaN
}
return intToValue(int64(s.charAt(pos) & 0xFFFF))
}
func (r *Runtime) stringproto_concat(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
strs := make([]valueString, len(call.Arguments)+1)
strs[0] = call.This.ToString()
_, allAscii := strs[0].(asciiString)
totalLen := strs[0].length()
for i, arg := range call.Arguments {
s := arg.ToString()
if allAscii {
_, allAscii = s.(asciiString)
}
strs[i+1] = s
totalLen += s.length()
}
if allAscii {
buf := bytes.NewBuffer(make([]byte, 0, totalLen))
for _, s := range strs {
buf.WriteString(s.String())
}
return asciiString(buf.String())
} else {
buf := make([]uint16, totalLen)
pos := int64(0)
for _, s := range strs {
switch s := s.(type) {
case asciiString:
for i := 0; i < len(s); i++ {
buf[pos] = uint16(s[i])
pos++
}
case unicodeString:
copy(buf[pos:], s)
pos += s.length()
}
}
return unicodeString(buf)
}
}
func (r *Runtime) stringproto_indexOf(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
value := call.This.ToString()
target := call.Argument(0).ToString()
pos := call.Argument(1).ToInteger()
if pos < 0 {
pos = 0
} else {
l := value.length()
if pos > l {
pos = l
}
}
return intToValue(value.index(target, pos))
}
func (r *Runtime) stringproto_lastIndexOf(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
value := call.This.ToString()
target := call.Argument(0).ToString()
numPos := call.Argument(1).ToNumber()
var pos int64
if f, ok := numPos.assertFloat(); ok && math.IsNaN(f) {
pos = value.length()
} else {
pos = numPos.ToInteger()
if pos < 0 {
pos = 0
} else {
l := value.length()
if pos > l {
pos = l
}
}
}
return intToValue(value.lastIndex(target, pos))
}
func (r *Runtime) stringproto_localeCompare(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
this := norm.NFD.String(call.This.String())
that := norm.NFD.String(call.Argument(0).String())
return intToValue(int64(r.collator().CompareString(this, that)))
}
func (r *Runtime) stringproto_match(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
regexp := call.Argument(0)
var rx *regexpObject
if regexp, ok := regexp.(*Object); ok {
rx, _ = regexp.self.(*regexpObject)
}
if rx == nil {
rx = r.builtin_newRegExp([]Value{regexp}).self.(*regexpObject)
}
if rx.global {
rx.putStr("lastIndex", intToValue(0), false)
var a []Value
var previousLastIndex int64
for {
match, result := rx.execRegexp(s)
if !match {
break
}
thisIndex := rx.getStr("lastIndex").ToInteger()
if thisIndex == previousLastIndex {
previousLastIndex++
rx.putStr("lastIndex", intToValue(previousLastIndex), false)
} else {
previousLastIndex = thisIndex
}
a = append(a, s.substring(int64(result[0]), int64(result[1])))
}
if len(a) == 0 {
return _null
}
return r.newArrayValues(a)
} else {
return rx.exec(s)
}
}
func (r *Runtime) stringproto_replace(call FunctionCall) Value {
s := call.This.ToString()
var str string
var isASCII bool
if astr, ok := s.(asciiString); ok {
str = string(astr)
isASCII = true
} else {
str = s.String()
}
searchValue := call.Argument(0)
replaceValue := call.Argument(1)
var found [][]int
if searchValue, ok := searchValue.(*Object); ok {
if regexp, ok := searchValue.self.(*regexpObject); ok {
find := 1
if regexp.global {
find = -1
}
if isASCII {
found = regexp.pattern.FindAllSubmatchIndexASCII(str, find)
} else {
found = regexp.pattern.FindAllSubmatchIndexUTF8(str, find)
}
if found == nil {
return s
}
}
}
if found == nil {
found = searchSubstringUTF8(str, searchValue.String())
}
if len(found) == 0 {
return s
}
var buf bytes.Buffer
lastIndex := 0
var rcall func(FunctionCall) Value
if replaceValue, ok := replaceValue.(*Object); ok {
if c, ok := replaceValue.self.assertCallable(); ok {
rcall = c
}
}
if rcall != nil {
for _, item := range found {
if item[0] != lastIndex {
buf.WriteString(str[lastIndex:item[0]])
}
matchCount := len(item) / 2
argumentList := make([]Value, matchCount+2)
for index := 0; index < matchCount; index++ {
offset := 2 * index
if item[offset] != -1 {
if isASCII {
argumentList[index] = asciiString(str[item[offset]:item[offset+1]])
} else {
argumentList[index] = newStringValue(str[item[offset]:item[offset+1]])
}
} else {
argumentList[index] = _undefined
}
}
argumentList[matchCount] = valueInt(item[0])
argumentList[matchCount+1] = s
replacement := rcall(FunctionCall{
This: _undefined,
Arguments: argumentList,
}).String()
buf.WriteString(replacement)
lastIndex = item[1]
}
} else {
newstring := replaceValue.String()
for _, item := range found {
if item[0] != lastIndex {
buf.WriteString(str[lastIndex:item[0]])
}
matches := len(item) / 2
for i := 0; i < len(newstring); i++ {
if newstring[i] == '$' && i < len(newstring)-1 {
ch := newstring[i+1]
switch ch {
case '$':
buf.WriteByte('$')
case '`':
buf.WriteString(str[0:item[0]])
case '\'':
buf.WriteString(str[item[1]:])
case '&':
buf.WriteString(str[item[0]:item[1]])
default:
matchNumber := 0
l := 0
for _, ch := range newstring[i+1:] {
if ch >= '0' && ch <= '9' {
m := matchNumber*10 + int(ch-'0')
if m >= matches {
break
}
matchNumber = m
l++
} else {
break
}
}
if l > 0 {
offset := 2 * matchNumber
if offset < len(item) && item[offset] != -1 {
buf.WriteString(str[item[offset]:item[offset+1]])
}
i += l - 1
} else {
buf.WriteByte('$')
buf.WriteByte(ch)
}
}
i++
} else {
buf.WriteByte(newstring[i])
}
}
lastIndex = item[1]
}
}
if lastIndex != len(str) {
buf.WriteString(str[lastIndex:])
}
return newStringValue(buf.String())
}
func (r *Runtime) stringproto_search(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
regexp := call.Argument(0)
var rx *regexpObject
if regexp, ok := regexp.(*Object); ok {
rx, _ = regexp.self.(*regexpObject)
}
if rx == nil {
rx = r.builtin_newRegExp([]Value{regexp}).self.(*regexpObject)
}
match, result := rx.execRegexp(s)
if !match {
return intToValue(-1)
}
return intToValue(int64(result[0]))
}
func (r *Runtime) stringproto_slice(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
l := s.length()
start := call.Argument(0).ToInteger()
var end int64
if arg1 := call.Argument(1); arg1 != _undefined {
end = arg1.ToInteger()
} else {
end = l
}
if start < 0 {
start += l
if start < 0 {
start = 0
}
} else {
if start > l {
start = l
}
}
if end < 0 {
end += l
if end < 0 {
end = 0
}
} else {
if end > l {
end = l
}
}
if end > start {
return s.substring(start, end)
}
return stringEmpty
}
func (r *Runtime) stringproto_split(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
separatorValue := call.Argument(0)
limitValue := call.Argument(1)
limit := -1
if limitValue != _undefined {
limit = int(toUInt32(limitValue))
}
if limit == 0 {
return r.newArrayValues(nil)
}
if separatorValue == _undefined {
return r.newArrayValues([]Value{s})
}
var search *regexpObject
if o, ok := separatorValue.(*Object); ok {
search, _ = o.self.(*regexpObject)
}
if search != nil {
targetLength := s.length()
valueArray := []Value{}
result := search.pattern.FindAllSubmatchIndex(s, -1)
lastIndex := 0
found := 0
for _, match := range result {
if match[0] == match[1] {
// FIXME Ugh, this is a hack
if match[0] == 0 || int64(match[0]) == targetLength {
continue
}
}
if lastIndex != match[0] {
valueArray = append(valueArray, s.substring(int64(lastIndex), int64(match[0])))
found++
} else if lastIndex == match[0] {
if lastIndex != -1 {
valueArray = append(valueArray, stringEmpty)
found++
}
}
lastIndex = match[1]
if found == limit {
goto RETURN
}
captureCount := len(match) / 2
for index := 1; index < captureCount; index++ {
offset := index * 2
var value Value
if match[offset] != -1 {
value = s.substring(int64(match[offset]), int64(match[offset+1]))
} else {
value = _undefined
}
valueArray = append(valueArray, value)
found++
if found == limit {
goto RETURN
}
}
}
if found != limit {
if int64(lastIndex) != targetLength {
valueArray = append(valueArray, s.substring(int64(lastIndex), targetLength))
} else {
valueArray = append(valueArray, stringEmpty)
}
}
RETURN:
return r.newArrayValues(valueArray)
} else {
separator := separatorValue.String()
excess := false
str := s.String()
if limit > len(str) {
limit = len(str)
}
splitLimit := limit
if limit > 0 {
splitLimit = limit + 1
excess = true
}
split := strings.SplitN(str, separator, splitLimit)
if excess && len(split) > limit {
split = split[:limit]
}
valueArray := make([]Value, len(split))
for index, value := range split {
valueArray[index] = newStringValue(value)
}
return r.newArrayValues(valueArray)
}
}
func (r *Runtime) stringproto_substring(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
l := s.length()
intStart := call.Argument(0).ToInteger()
var intEnd int64
if end := call.Argument(1); end != _undefined {
intEnd = end.ToInteger()
} else {
intEnd = l
}
if intStart < 0 {
intStart = 0
} else if intStart > l {
intStart = l
}
if intEnd < 0 {
intEnd = 0
} else if intEnd > l {
intEnd = l
}
if intStart > intEnd {
intStart, intEnd = intEnd, intStart
}
return s.substring(intStart, intEnd)
}
func (r *Runtime) stringproto_toLowerCase(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
return s.toLower()
}
func (r *Runtime) stringproto_toUpperCase(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
return s.toUpper()
}
func (r *Runtime) stringproto_trim(call FunctionCall) Value {
r.checkObjectCoercible(call.This)
s := call.This.ToString()
return newStringValue(strings.Trim(s.String(), parser.WhitespaceChars))
}
func (r *Runtime) stringproto_substr(call FunctionCall) Value {
s := call.This.ToString()
start := call.Argument(0).ToInteger()
var length int64
sl := int64(s.length())
if arg := call.Argument(1); arg != _undefined {
length = arg.ToInteger()
} else {
length = sl
}
if start < 0 {
start = max(sl+start, 0)
}
length = min(max(length, 0), sl-start)
if length <= 0 {
return stringEmpty
}
return s.substring(start, start+length)
}
func (r *Runtime) initString() {
r.global.StringPrototype = r.builtin_newString([]Value{stringEmpty})
o := r.global.StringPrototype.self
o.(*stringObject).prototype = r.global.ObjectPrototype
o._putProp("toString", r.newNativeFunc(r.stringproto_toString, nil, "toString", nil, 0), true, false, true)
o._putProp("valueOf", r.newNativeFunc(r.stringproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
o._putProp("charAt", r.newNativeFunc(r.stringproto_charAt, nil, "charAt", nil, 1), true, false, true)
o._putProp("charCodeAt", r.newNativeFunc(r.stringproto_charCodeAt, nil, "charCodeAt", nil, 1), true, false, true)
o._putProp("concat", r.newNativeFunc(r.stringproto_concat, nil, "concat", nil, 1), true, false, true)
o._putProp("indexOf", r.newNativeFunc(r.stringproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
o._putProp("lastIndexOf", r.newNativeFunc(r.stringproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
o._putProp("localeCompare", r.newNativeFunc(r.stringproto_localeCompare, nil, "localeCompare", nil, 1), true, false, true)
o._putProp("match", r.newNativeFunc(r.stringproto_match, nil, "match", nil, 1), true, false, true)
o._putProp("replace", r.newNativeFunc(r.stringproto_replace, nil, "replace", nil, 2), true, false, true)
o._putProp("search", r.newNativeFunc(r.stringproto_search, nil, "search", nil, 1), true, false, true)
o._putProp("slice", r.newNativeFunc(r.stringproto_slice, nil, "slice", nil, 2), true, false, true)
o._putProp("split", r.newNativeFunc(r.stringproto_split, nil, "split", nil, 2), true, false, true)
o._putProp("substring", r.newNativeFunc(r.stringproto_substring, nil, "substring", nil, 2), true, false, true)
o._putProp("toLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLowerCase", nil, 0), true, false, true)
o._putProp("toLocaleLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLocaleLowerCase", nil, 0), true, false, true)
o._putProp("toUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toUpperCase", nil, 0), true, false, true)
o._putProp("toLocaleUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toLocaleUpperCase", nil, 0), true, false, true)
o._putProp("trim", r.newNativeFunc(r.stringproto_trim, nil, "trim", nil, 0), true, false, true)
// Annex B
o._putProp("substr", r.newNativeFunc(r.stringproto_substr, nil, "substr", nil, 2), true, false, true)
r.global.String = r.newNativeFunc(r.builtin_String, r.builtin_newString, "String", r.global.StringPrototype, 1)
o = r.global.String.self
o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, nil, "fromCharCode", nil, 1), true, false, true)
r.addToGlobal("String", r.global.String)
r.stringSingleton = r.builtin_new(r.global.String, nil).self.(*stringObject)
}

105
vendor/github.com/dop251/goja/builtin_typedarrays.go generated vendored Normal file
View file

@ -0,0 +1,105 @@
package goja
type objectArrayBuffer struct {
baseObject
data []byte
}
func (o *objectArrayBuffer) export() interface{} {
return o.data
}
func (r *Runtime) _newArrayBuffer(proto *Object, o *Object) *objectArrayBuffer {
if o == nil {
o = &Object{runtime: r}
}
b := &objectArrayBuffer{
baseObject: baseObject{
class: classObject,
val: o,
prototype: proto,
extensible: true,
},
}
o.self = b
b.init()
return b
}
func (r *Runtime) builtin_ArrayBuffer(args []Value, proto *Object) *Object {
b := r._newArrayBuffer(proto, nil)
if len(args) > 0 {
b.data = make([]byte, toLength(args[0]))
}
return b.val
}
func (r *Runtime) arrayBufferProto_getByteLength(call FunctionCall) Value {
o := r.toObject(call.This)
if b, ok := o.self.(*objectArrayBuffer); ok {
return intToValue(int64(len(b.data)))
}
r.typeErrorResult(true, "Object is not ArrayBuffer: %s", o)
panic("unreachable")
}
func (r *Runtime) arrayBufferProto_slice(call FunctionCall) Value {
o := r.toObject(call.This)
if b, ok := o.self.(*objectArrayBuffer); ok {
l := int64(len(b.data))
start := toLength(call.Argument(0))
if start < 0 {
start = l + start
}
if start < 0 {
start = 0
} else if start > l {
start = l
}
var stop int64
if arg := call.Argument(1); arg != _undefined {
stop = toLength(arg)
if stop < 0 {
stop = int64(len(b.data)) + stop
}
if stop < 0 {
stop = 0
} else if stop > l {
stop = l
}
} else {
stop = l
}
ret := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
if stop > start {
ret.data = b.data[start:stop]
}
return ret.val
}
r.typeErrorResult(true, "Object is not ArrayBuffer: %s", o)
panic("unreachable")
}
func (r *Runtime) createArrayBufferProto(val *Object) objectImpl {
b := r._newArrayBuffer(r.global.Object, val)
byteLengthProp := &valueProperty{
accessor: true,
configurable: true,
getterFunc: r.newNativeFunc(r.arrayBufferProto_getByteLength, nil, "get byteLength", nil, 0),
}
b._put("byteLength", byteLengthProp)
b._putProp("slice", r.newNativeFunc(r.arrayBufferProto_slice, nil, "slice", nil, 2), true, false, true)
return b
}
func (r *Runtime) initTypedArrays() {
r.global.ArrayBufferPrototype = r.newLazyObject(r.createArrayBufferProto)
r.global.ArrayBuffer = r.newNativeFuncConstruct(r.builtin_ArrayBuffer, "ArrayBuffer", r.global.ArrayBufferPrototype, 1)
r.addToGlobal("ArrayBuffer", r.global.ArrayBuffer)
}

461
vendor/github.com/dop251/goja/compiler.go generated vendored Normal file
View file

@ -0,0 +1,461 @@
package goja
import (
"fmt"
"github.com/dop251/goja/ast"
"github.com/dop251/goja/file"
"sort"
"strconv"
)
const (
blockLoop = iota
blockTry
blockBranch
blockSwitch
blockWith
)
type CompilerError struct {
Message string
File *SrcFile
Offset int
}
type CompilerSyntaxError struct {
CompilerError
}
type CompilerReferenceError struct {
CompilerError
}
type srcMapItem struct {
pc int
srcPos int
}
type Program struct {
code []instruction
values []Value
funcName string
src *SrcFile
srcMap []srcMapItem
}
type compiler struct {
p *Program
scope *scope
block *block
blockStart int
enumGetExpr compiledEnumGetExpr
evalVM *vm
}
type scope struct {
names map[string]uint32
outer *scope
strict bool
eval bool
lexical bool
dynamic bool
accessed bool
argsNeeded bool
thisNeeded bool
namesMap map[string]string
lastFreeTmp int
}
type block struct {
typ int
label string
needResult bool
cont int
breaks []int
conts []int
outer *block
}
func (c *compiler) leaveBlock() {
lbl := len(c.p.code)
for _, item := range c.block.breaks {
c.p.code[item] = jump(lbl - item)
}
if c.block.typ == blockLoop {
for _, item := range c.block.conts {
c.p.code[item] = jump(c.block.cont - item)
}
}
c.block = c.block.outer
}
func (e *CompilerSyntaxError) Error() string {
if e.File != nil {
return fmt.Sprintf("SyntaxError: %s at %s", e.Message, e.File.Position(e.Offset))
}
return fmt.Sprintf("SyntaxError: %s", e.Message)
}
func (e *CompilerReferenceError) Error() string {
return fmt.Sprintf("ReferenceError: %s", e.Message)
}
func (c *compiler) newScope() {
strict := false
if c.scope != nil {
strict = c.scope.strict
}
c.scope = &scope{
outer: c.scope,
names: make(map[string]uint32),
strict: strict,
namesMap: make(map[string]string),
}
}
func (c *compiler) popScope() {
c.scope = c.scope.outer
}
func newCompiler() *compiler {
c := &compiler{
p: &Program{},
}
c.enumGetExpr.init(c, file.Idx(0))
c.newScope()
c.scope.dynamic = true
return c
}
func (p *Program) defineLiteralValue(val Value) uint32 {
for idx, v := range p.values {
if v.SameAs(val) {
return uint32(idx)
}
}
idx := uint32(len(p.values))
p.values = append(p.values, val)
return idx
}
func (p *Program) dumpCode(logger func(format string, args ...interface{})) {
p._dumpCode("", logger)
}
func (p *Program) _dumpCode(indent string, logger func(format string, args ...interface{})) {
logger("values: %+v", p.values)
for pc, ins := range p.code {
logger("%s %d: %T(%v)", indent, pc, ins, ins)
if f, ok := ins.(*newFunc); ok {
f.prg._dumpCode(indent+">", logger)
}
}
}
func (p *Program) sourceOffset(pc int) int {
i := sort.Search(len(p.srcMap), func(idx int) bool {
return p.srcMap[idx].pc > pc
}) - 1
if i >= 0 {
return p.srcMap[i].srcPos
}
return 0
}
func (s *scope) isFunction() bool {
if !s.lexical {
return s.outer != nil
}
return s.outer.isFunction()
}
func (s *scope) lookupName(name string) (idx uint32, found, noDynamics bool) {
var level uint32 = 0
noDynamics = true
for curScope := s; curScope != nil; curScope = curScope.outer {
if curScope != s {
curScope.accessed = true
}
if curScope.dynamic {
noDynamics = false
} else {
var mapped string
if m, exists := curScope.namesMap[name]; exists {
mapped = m
} else {
mapped = name
}
if i, exists := curScope.names[mapped]; exists {
idx = i | (level << 24)
found = true
return
}
}
if name == "arguments" && !s.lexical && s.isFunction() {
s.argsNeeded = true
s.accessed = true
idx, _ = s.bindName(name)
found = true
return
}
level++
}
return
}
func (s *scope) bindName(name string) (uint32, bool) {
if s.lexical {
return s.outer.bindName(name)
}
if idx, exists := s.names[name]; exists {
return idx, false
}
idx := uint32(len(s.names))
s.names[name] = idx
return idx, true
}
func (s *scope) bindNameShadow(name string) (uint32, bool) {
if s.lexical {
return s.outer.bindName(name)
}
unique := true
if idx, exists := s.names[name]; exists {
unique = false
// shadow the var
delete(s.names, name)
n := strconv.Itoa(int(idx))
s.names[n] = idx
}
idx := uint32(len(s.names))
s.names[name] = idx
return idx, unique
}
func (c *compiler) markBlockStart() {
c.blockStart = len(c.p.code)
}
func (c *compiler) compile(in *ast.Program) {
c.p.src = NewSrcFile(in.File.Name(), in.File.Source(), in.SourceMap)
if len(in.Body) > 0 {
if !c.scope.strict {
c.scope.strict = c.isStrict(in.Body)
}
}
c.compileDeclList(in.DeclarationList, false)
c.compileFunctions(in.DeclarationList)
c.markBlockStart()
c.compileStatements(in.Body, true)
c.p.code = append(c.p.code, halt)
code := c.p.code
c.p.code = make([]instruction, 0, len(code)+len(c.scope.names)+2)
if c.scope.eval {
if !c.scope.strict {
c.emit(jne(2), newStash)
} else {
c.emit(pop, newStash)
}
}
l := len(c.p.code)
c.p.code = c.p.code[:l+len(c.scope.names)]
for name, nameIdx := range c.scope.names {
c.p.code[l+int(nameIdx)] = bindName(name)
}
c.p.code = append(c.p.code, code...)
for i, _ := range c.p.srcMap {
c.p.srcMap[i].pc += len(c.scope.names)
}
}
func (c *compiler) compileDeclList(v []ast.Declaration, inFunc bool) {
for _, value := range v {
switch value := value.(type) {
case *ast.FunctionDeclaration:
c.compileFunctionDecl(value)
case *ast.VariableDeclaration:
c.compileVarDecl(value, inFunc)
default:
panic(fmt.Errorf("Unsupported declaration: %T", value))
}
}
}
func (c *compiler) compileFunctions(v []ast.Declaration) {
for _, value := range v {
if value, ok := value.(*ast.FunctionDeclaration); ok {
c.compileFunction(value)
}
}
}
func (c *compiler) compileVarDecl(v *ast.VariableDeclaration, inFunc bool) {
for _, item := range v.List {
if c.scope.strict {
c.checkIdentifierLName(item.Name, int(item.Idx)-1)
c.checkIdentifierName(item.Name, int(item.Idx)-1)
}
if !inFunc || item.Name != "arguments" {
idx, ok := c.scope.bindName(item.Name)
_ = idx
//log.Printf("Define var: %s: %x", item.Name, idx)
if !ok {
// TODO: error
}
}
}
}
func (c *compiler) addDecls() []instruction {
code := make([]instruction, len(c.scope.names))
for name, nameIdx := range c.scope.names {
code[nameIdx] = bindName(name)
}
return code
}
func (c *compiler) convertInstrToStashless(instr uint32, args int) (newIdx int, convert bool) {
level := instr >> 24
idx := instr & 0x00FFFFFF
if level > 0 {
level--
newIdx = int((level << 24) | idx)
} else {
iidx := int(idx)
if iidx < args {
newIdx = -iidx - 1
} else {
newIdx = iidx - args + 1
}
convert = true
}
return
}
func (c *compiler) convertFunctionToStashless(code []instruction, args int) {
code[0] = enterFuncStashless{stackSize: uint32(len(c.scope.names) - args), args: uint32(args)}
for pc := 1; pc < len(code); pc++ {
instr := code[pc]
if instr == ret {
code[pc] = retStashless
}
switch instr := instr.(type) {
case getLocal:
if newIdx, convert := c.convertInstrToStashless(uint32(instr), args); convert {
code[pc] = loadStack(newIdx)
} else {
code[pc] = getLocal(newIdx)
}
case setLocal:
if newIdx, convert := c.convertInstrToStashless(uint32(instr), args); convert {
code[pc] = storeStack(newIdx)
} else {
code[pc] = setLocal(newIdx)
}
case setLocalP:
if newIdx, convert := c.convertInstrToStashless(uint32(instr), args); convert {
code[pc] = storeStackP(newIdx)
} else {
code[pc] = setLocalP(newIdx)
}
case getVar:
level := instr.idx >> 24
idx := instr.idx & 0x00FFFFFF
level--
instr.idx = level<<24 | idx
code[pc] = instr
case setVar:
level := instr.idx >> 24
idx := instr.idx & 0x00FFFFFF
level--
instr.idx = level<<24 | idx
code[pc] = instr
}
}
}
func (c *compiler) compileFunctionDecl(v *ast.FunctionDeclaration) {
idx, ok := c.scope.bindName(v.Function.Name.Name)
if !ok {
// TODO: error
}
_ = idx
// log.Printf("Define function: %s: %x", v.Function.Name.Name, idx)
}
func (c *compiler) compileFunction(v *ast.FunctionDeclaration) {
e := &compiledIdentifierExpr{
name: v.Function.Name.Name,
}
e.init(c, v.Function.Idx0())
e.emitSetter(c.compileFunctionLiteral(v.Function, false))
c.emit(pop)
}
func (c *compiler) emit(instructions ...instruction) {
c.p.code = append(c.p.code, instructions...)
}
func (c *compiler) throwSyntaxError(offset int, format string, args ...interface{}) {
panic(&CompilerSyntaxError{
CompilerError: CompilerError{
File: c.p.src,
Offset: offset,
Message: fmt.Sprintf(format, args...),
},
})
}
func (c *compiler) isStrict(list []ast.Statement) bool {
for _, st := range list {
if st, ok := st.(*ast.ExpressionStatement); ok {
if e, ok := st.Expression.(*ast.StringLiteral); ok {
if e.Literal == `"use strict"` || e.Literal == `'use strict'` {
return true
}
} else {
break
}
} else {
break
}
}
return false
}
func (c *compiler) isStrictStatement(s ast.Statement) bool {
if s, ok := s.(*ast.BlockStatement); ok {
return c.isStrict(s.List)
}
return false
}
func (c *compiler) checkIdentifierName(name string, offset int) {
switch name {
case "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield":
c.throwSyntaxError(offset, "Unexpected strict mode reserved word")
}
}
func (c *compiler) checkIdentifierLName(name string, offset int) {
switch name {
case "eval", "arguments":
c.throwSyntaxError(offset, "Assignment to eval or arguments is not allowed in strict mode")
}
}

1562
vendor/github.com/dop251/goja/compiler_expr.go generated vendored Normal file

File diff suppressed because it is too large Load diff

796
vendor/github.com/dop251/goja/compiler_stmt.go generated vendored Normal file
View file

@ -0,0 +1,796 @@
package goja
import (
"fmt"
"github.com/dop251/goja/ast"
"github.com/dop251/goja/file"
"github.com/dop251/goja/token"
"strconv"
)
func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
// log.Printf("compileStatement(): %T", v)
switch v := v.(type) {
case *ast.BlockStatement:
c.compileBlockStatement(v, needResult)
case *ast.ExpressionStatement:
c.compileExpressionStatement(v, needResult)
case *ast.VariableStatement:
c.compileVariableStatement(v, needResult)
case *ast.ReturnStatement:
c.compileReturnStatement(v)
case *ast.IfStatement:
c.compileIfStatement(v, needResult)
case *ast.DoWhileStatement:
c.compileDoWhileStatement(v, needResult)
case *ast.ForStatement:
c.compileForStatement(v, needResult)
case *ast.ForInStatement:
c.compileForInStatement(v, needResult)
case *ast.WhileStatement:
c.compileWhileStatement(v, needResult)
case *ast.BranchStatement:
c.compileBranchStatement(v, needResult)
case *ast.TryStatement:
c.compileTryStatement(v)
if needResult {
c.emit(loadUndef)
}
case *ast.ThrowStatement:
c.compileThrowStatement(v)
case *ast.SwitchStatement:
c.compileSwitchStatement(v, needResult)
case *ast.LabelledStatement:
c.compileLabeledStatement(v, needResult)
case *ast.EmptyStatement:
c.compileEmptyStatement(needResult)
case *ast.WithStatement:
c.compileWithStatement(v, needResult)
case *ast.DebuggerStatement:
default:
panic(fmt.Errorf("Unknown statement type: %T", v))
}
}
func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) {
label := v.Label.Name
for b := c.block; b != nil; b = b.outer {
if b.label == label {
c.throwSyntaxError(int(v.Label.Idx-1), "Label '%s' has already been declared", label)
}
}
switch s := v.Statement.(type) {
case *ast.ForInStatement:
c.compileLabeledForInStatement(s, needResult, label)
case *ast.ForStatement:
c.compileLabeledForStatement(s, needResult, label)
case *ast.WhileStatement:
c.compileLabeledWhileStatement(s, needResult, label)
case *ast.DoWhileStatement:
c.compileLabeledDoWhileStatement(s, needResult, label)
default:
c.compileGenericLabeledStatement(v.Statement, needResult, label)
}
}
func (c *compiler) compileTryStatement(v *ast.TryStatement) {
if c.scope.strict && v.Catch != nil {
switch v.Catch.Parameter.Name {
case "arguments", "eval":
c.throwSyntaxError(int(v.Catch.Parameter.Idx)-1, "Catch variable may not be eval or arguments in strict mode")
}
}
c.block = &block{
typ: blockTry,
outer: c.block,
}
lbl := len(c.p.code)
c.emit(nil)
c.compileStatement(v.Body, false)
c.emit(halt)
lbl2 := len(c.p.code)
c.emit(nil)
var catchOffset int
dynamicCatch := true
if v.Catch != nil {
dyn := nearestNonLexical(c.scope).dynamic
accessed := c.scope.accessed
c.newScope()
c.scope.bindName(v.Catch.Parameter.Name)
c.scope.lexical = true
start := len(c.p.code)
c.emit(nil)
catchOffset = len(c.p.code) - lbl
c.emit(enterCatch(v.Catch.Parameter.Name))
c.compileStatement(v.Catch.Body, false)
dyn1 := c.scope.dynamic
accessed1 := c.scope.accessed
c.popScope()
if !dyn && !dyn1 && !accessed1 {
c.scope.accessed = accessed
dynamicCatch = false
code := c.p.code[start+1:]
m := make(map[uint32]uint32)
remap := func(instr uint32) uint32 {
level := instr >> 24
idx := instr & 0x00FFFFFF
if level > 0 {
level--
return (level << 24) | idx
} else {
// remap
newIdx, exists := m[idx]
if !exists {
exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
c.scope.lastFreeTmp++
newIdx, _ = c.scope.bindName(exname)
m[idx] = newIdx
}
return newIdx
}
}
for pc, instr := range code {
switch instr := instr.(type) {
case getLocal:
code[pc] = getLocal(remap(uint32(instr)))
case setLocal:
code[pc] = setLocal(remap(uint32(instr)))
case setLocalP:
code[pc] = setLocalP(remap(uint32(instr)))
}
}
if catchVarIdx, exists := m[0]; exists {
c.p.code[start] = setLocal(catchVarIdx)
c.p.code[start+1] = pop
catchOffset--
} else {
c.p.code[start+1] = nil
catchOffset++
}
} else {
c.scope.accessed = true
}
/*
if true/*sc.dynamic/ {
dynamicCatch = true
c.scope.accessed = true
c.newScope()
c.scope.bindName(v.Catch.Parameter.Name)
c.scope.lexical = true
c.emit(enterCatch(v.Catch.Parameter.Name))
c.compileStatement(v.Catch.Body, false)
c.popScope()
} else {
exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
c.scope.lastFreeTmp++
catchVarIdx, _ := c.scope.bindName(exname)
c.emit(setLocal(catchVarIdx), pop)
saved, wasSaved := c.scope.namesMap[v.Catch.Parameter.Name]
c.scope.namesMap[v.Catch.Parameter.Name] = exname
c.compileStatement(v.Catch.Body, false)
if wasSaved {
c.scope.namesMap[v.Catch.Parameter.Name] = saved
} else {
delete(c.scope.namesMap, v.Catch.Parameter.Name)
}
c.scope.lastFreeTmp--
}*/
c.emit(halt)
}
var finallyOffset int
if v.Finally != nil {
lbl1 := len(c.p.code)
c.emit(nil)
finallyOffset = len(c.p.code) - lbl
c.compileStatement(v.Finally, false)
c.emit(halt, retFinally)
c.p.code[lbl1] = jump(len(c.p.code) - lbl1)
}
c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset), dynamic: dynamicCatch}
c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
c.leaveBlock()
}
func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
//c.p.srcMap = append(c.p.srcMap, srcMapItem{pc: len(c.p.code), srcPos: int(v.Throw) - 1})
c.compileExpression(v.Argument).emitGetter(true)
c.emit(throw)
}
func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) {
c.compileLabeledDoWhileStatement(v, needResult, "")
}
func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label string) {
c.block = &block{
typ: blockLoop,
outer: c.block,
label: label,
needResult: needResult,
}
if needResult {
c.emit(jump(2))
}
start := len(c.p.code)
if needResult {
c.emit(pop)
}
c.markBlockStart()
c.compileStatement(v.Body, needResult)
c.block.cont = len(c.p.code)
c.emitExpr(c.compileExpression(v.Test), true)
c.emit(jeq(start - len(c.p.code)))
c.leaveBlock()
}
func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
c.compileLabeledForStatement(v, needResult, "")
}
func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label string) {
c.block = &block{
typ: blockLoop,
outer: c.block,
label: label,
needResult: needResult,
}
if v.Initializer != nil {
c.compileExpression(v.Initializer).emitGetter(false)
}
if needResult {
c.emit(loadUndef) // initial result
}
start := len(c.p.code)
c.markBlockStart()
var j int
testConst := false
if v.Test != nil {
expr := c.compileExpression(v.Test)
if expr.constant() {
r, ex := c.evalConst(expr)
if ex == nil {
if r.ToBoolean() {
testConst = true
} else {
// TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
/*
p := c.p
c.p = &program{}
c.compileStatement(v.Body, false)
if v.Update != nil {
c.compileExpression(v.Update).emitGetter(false)
}
c.p = p*/
goto end
}
} else {
expr.addSrcMap()
c.emitThrow(ex.val)
goto end
}
} else {
expr.emitGetter(true)
j = len(c.p.code)
c.emit(nil)
}
}
if needResult {
c.emit(pop) // remove last result
}
c.markBlockStart()
c.compileStatement(v.Body, needResult)
c.block.cont = len(c.p.code)
if v.Update != nil {
c.compileExpression(v.Update).emitGetter(false)
}
c.emit(jump(start - len(c.p.code)))
if v.Test != nil {
if !testConst {
c.p.code[j] = jne(len(c.p.code) - j)
}
}
end:
c.leaveBlock()
c.markBlockStart()
}
func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
c.compileLabeledForInStatement(v, needResult, "")
}
func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label string) {
c.block = &block{
typ: blockLoop,
outer: c.block,
label: label,
needResult: needResult,
}
c.compileExpression(v.Source).emitGetter(true)
c.emit(enumerate)
if needResult {
c.emit(loadUndef)
}
start := len(c.p.code)
c.markBlockStart()
c.block.cont = start
c.emit(nil)
c.compileExpression(v.Into).emitSetter(&c.enumGetExpr)
c.emit(pop)
if needResult {
c.emit(pop) // remove last result
}
c.markBlockStart()
c.compileStatement(v.Body, needResult)
c.emit(jump(start - len(c.p.code)))
c.p.code[start] = enumNext(len(c.p.code) - start)
c.leaveBlock()
c.markBlockStart()
c.emit(enumPop)
}
func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
c.compileLabeledWhileStatement(v, needResult, "")
}
func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label string) {
c.block = &block{
typ: blockLoop,
outer: c.block,
label: label,
needResult: needResult,
}
if needResult {
c.emit(loadUndef)
}
start := len(c.p.code)
c.markBlockStart()
c.block.cont = start
expr := c.compileExpression(v.Test)
testTrue := false
var j int
if expr.constant() {
if t, ex := c.evalConst(expr); ex == nil {
if t.ToBoolean() {
testTrue = true
} else {
p := c.p
c.p = &Program{}
c.compileStatement(v.Body, false)
c.p = p
goto end
}
} else {
c.emitThrow(ex.val)
goto end
}
} else {
expr.emitGetter(true)
j = len(c.p.code)
c.emit(nil)
}
if needResult {
c.emit(pop)
}
c.markBlockStart()
c.compileStatement(v.Body, needResult)
c.emit(jump(start - len(c.p.code)))
if !testTrue {
c.p.code[j] = jne(len(c.p.code) - j)
}
end:
c.leaveBlock()
c.markBlockStart()
}
func (c *compiler) compileEmptyStatement(needResult bool) {
if needResult {
if len(c.p.code) == c.blockStart {
// first statement in block, use undefined as result
c.emit(loadUndef)
}
}
}
func (c *compiler) compileBranchStatement(v *ast.BranchStatement, needResult bool) {
switch v.Token {
case token.BREAK:
c.compileBreak(v.Label, v.Idx)
case token.CONTINUE:
c.compileContinue(v.Label, v.Idx)
default:
panic(fmt.Errorf("Unknown branch statement token: %s", v.Token.String()))
}
}
func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
switch st.Token {
case token.BREAK:
return c.findBreakBlock(st.Label)
case token.CONTINUE:
return c.findContinueBlock(st.Label)
}
return nil
}
func (c *compiler) findContinueBlock(label *ast.Identifier) (block *block) {
if label != nil {
for b := c.block; b != nil; b = b.outer {
if b.typ == blockLoop && b.label == label.Name {
block = b
break
}
}
} else {
// find the nearest loop
for b := c.block; b != nil; b = b.outer {
if b.typ == blockLoop {
block = b
break
}
}
}
return
}
func (c *compiler) findBreakBlock(label *ast.Identifier) (block *block) {
if label != nil {
for b := c.block; b != nil; b = b.outer {
if b.label == label.Name {
block = b
break
}
}
} else {
// find the nearest loop or switch
L:
for b := c.block; b != nil; b = b.outer {
switch b.typ {
case blockLoop, blockSwitch:
block = b
break L
}
}
}
return
}
func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
var block *block
if label != nil {
for b := c.block; b != nil; b = b.outer {
switch b.typ {
case blockTry:
c.emit(halt)
case blockWith:
c.emit(leaveWith)
}
if b.label == label.Name {
block = b
break
}
}
} else {
// find the nearest loop or switch
L:
for b := c.block; b != nil; b = b.outer {
switch b.typ {
case blockTry:
c.emit(halt)
case blockWith:
c.emit(leaveWith)
case blockLoop, blockSwitch:
block = b
break L
}
}
}
if block != nil {
if len(c.p.code) == c.blockStart && block.needResult {
c.emit(loadUndef)
}
block.breaks = append(block.breaks, len(c.p.code))
c.emit(nil)
} else {
c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
}
}
func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
var block *block
if label != nil {
for b := c.block; b != nil; b = b.outer {
if b.typ == blockTry {
c.emit(halt)
} else if b.typ == blockLoop && b.label == label.Name {
block = b
break
}
}
} else {
// find the nearest loop
for b := c.block; b != nil; b = b.outer {
if b.typ == blockTry {
c.emit(halt)
} else if b.typ == blockLoop {
block = b
break
}
}
}
if block != nil {
if len(c.p.code) == c.blockStart && block.needResult {
c.emit(loadUndef)
}
block.conts = append(block.conts, len(c.p.code))
c.emit(nil)
} else {
c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
}
}
func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
test := c.compileExpression(v.Test)
if test.constant() {
r, ex := c.evalConst(test)
if ex != nil {
test.addSrcMap()
c.emitThrow(ex.val)
return
}
if r.ToBoolean() {
c.markBlockStart()
c.compileStatement(v.Consequent, needResult)
if v.Alternate != nil {
p := c.p
c.p = &Program{}
c.markBlockStart()
c.compileStatement(v.Alternate, false)
c.p = p
}
} else {
// TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
p := c.p
c.p = &Program{}
c.compileStatement(v.Consequent, false)
c.p = p
if v.Alternate != nil {
c.compileStatement(v.Alternate, needResult)
} else {
if needResult {
c.emit(loadUndef)
}
}
}
return
}
test.emitGetter(true)
jmp := len(c.p.code)
c.emit(nil)
c.markBlockStart()
c.compileStatement(v.Consequent, needResult)
if v.Alternate != nil {
jmp1 := len(c.p.code)
c.emit(nil)
c.p.code[jmp] = jne(len(c.p.code) - jmp)
c.markBlockStart()
c.compileStatement(v.Alternate, needResult)
c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
c.markBlockStart()
} else {
c.p.code[jmp] = jne(len(c.p.code) - jmp)
c.markBlockStart()
if needResult {
c.emit(loadUndef)
}
}
}
func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
if v.Argument != nil {
c.compileExpression(v.Argument).emitGetter(true)
//c.emit(checkResolve)
} else {
c.emit(loadUndef)
}
for b := c.block; b != nil; b = b.outer {
if b.typ == blockTry {
c.emit(halt)
}
}
c.emit(ret)
}
func (c *compiler) compileVariableStatement(v *ast.VariableStatement, needResult bool) {
for _, expr := range v.List {
c.compileExpression(expr).emitGetter(false)
}
if needResult {
c.emit(loadUndef)
}
}
func (c *compiler) getFirstNonEmptyStatement(st ast.Statement) ast.Statement {
switch st := st.(type) {
case *ast.BlockStatement:
return c.getFirstNonEmptyStatementList(st.List)
case *ast.LabelledStatement:
return c.getFirstNonEmptyStatement(st.Statement)
}
return st
}
func (c *compiler) getFirstNonEmptyStatementList(list []ast.Statement) ast.Statement {
for _, st := range list {
switch st := st.(type) {
case *ast.EmptyStatement:
continue
case *ast.BlockStatement:
return c.getFirstNonEmptyStatementList(st.List)
case *ast.LabelledStatement:
return c.getFirstNonEmptyStatement(st.Statement)
}
return st
}
return nil
}
func (c *compiler) compileStatements(list []ast.Statement, needResult bool) {
if len(list) > 0 {
cur := list[0]
for idx := 0; idx < len(list); {
var next ast.Statement
// find next non-empty statement
for idx++; idx < len(list); idx++ {
if _, empty := list[idx].(*ast.EmptyStatement); !empty {
next = list[idx]
break
}
}
if next != nil {
bs := c.getFirstNonEmptyStatement(next)
if bs, ok := bs.(*ast.BranchStatement); ok {
block := c.findBranchBlock(bs)
if block != nil {
c.compileStatement(cur, block.needResult)
cur = next
continue
}
}
c.compileStatement(cur, false)
cur = next
} else {
c.compileStatement(cur, needResult)
}
}
} else {
if needResult {
c.emit(loadUndef)
}
}
}
func (c *compiler) compileGenericLabeledStatement(v ast.Statement, needResult bool, label string) {
c.block = &block{
typ: blockBranch,
outer: c.block,
label: label,
needResult: needResult,
}
c.compileStatement(v, needResult)
c.leaveBlock()
}
func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) {
c.compileStatements(v.List, needResult)
}
func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) {
expr := c.compileExpression(v.Expression)
if expr.constant() {
c.emitConst(expr, needResult)
} else {
expr.emitGetter(needResult)
}
}
func (c *compiler) compileWithStatement(v *ast.WithStatement, needResult bool) {
if c.scope.strict {
c.throwSyntaxError(int(v.With)-1, "Strict mode code may not include a with statement")
return
}
c.compileExpression(v.Object).emitGetter(true)
c.emit(enterWith)
c.block = &block{
outer: c.block,
typ: blockWith,
needResult: needResult,
}
c.newScope()
c.scope.dynamic = true
c.scope.lexical = true
c.compileStatement(v.Body, needResult)
c.emit(leaveWith)
c.leaveBlock()
c.popScope()
}
func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult bool) {
c.block = &block{
typ: blockSwitch,
outer: c.block,
needResult: needResult,
}
c.compileExpression(v.Discriminant).emitGetter(true)
jumps := make([]int, len(v.Body))
for i, s := range v.Body {
if s.Test != nil {
c.emit(dup)
c.compileExpression(s.Test).emitGetter(true)
c.emit(op_strict_eq)
c.emit(jne(3), pop)
jumps[i] = len(c.p.code)
c.emit(nil)
}
}
c.emit(pop)
jumpNoMatch := -1
if v.Default != -1 {
if v.Default != 0 {
jumps[v.Default] = len(c.p.code)
c.emit(nil)
}
} else {
jumpNoMatch = len(c.p.code)
c.emit(nil)
}
for i, s := range v.Body {
if s.Test != nil || i != 0 {
c.p.code[jumps[i]] = jump(len(c.p.code) - jumps[i])
c.markBlockStart()
}
nr := false
c.markBlockStart()
if needResult {
if i < len(v.Body)-1 {
st := c.getFirstNonEmptyStatementList(v.Body[i+1].Consequent)
if st, ok := st.(*ast.BranchStatement); ok && st.Token == token.BREAK {
if c.findBreakBlock(st.Label) != nil {
stmts := append(s.Consequent, st)
c.compileStatements(stmts, false)
continue
}
}
} else {
nr = true
}
}
c.compileStatements(s.Consequent, nr)
}
if jumpNoMatch != -1 {
if needResult {
c.emit(jump(2))
}
c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch)
if needResult {
c.emit(loadUndef)
}
}
c.leaveBlock()
c.markBlockStart()
}

104
vendor/github.com/dop251/goja/date.go generated vendored Normal file
View file

@ -0,0 +1,104 @@
package goja
import (
"time"
)
const (
dateTimeLayout = "Mon Jan 02 2006 15:04:05 GMT-0700 (MST)"
isoDateTimeLayout = "2006-01-02T15:04:05.000Z"
dateLayout = "Mon Jan 02 2006"
timeLayout = "15:04:05 GMT-0700 (MST)"
datetimeLayout_en_GB = "01/02/2006, 15:04:05"
dateLayout_en_GB = "01/02/2006"
timeLayout_en_GB = "15:04:05"
)
type dateObject struct {
baseObject
time time.Time
isSet bool
}
var (
dateLayoutList = []string{
"2006-01-02T15:04:05.000Z0700",
"2006-01-02T15:04:05.000",
"2006-01-02T15:04:05Z0700",
"2006-01-02T15:04:05",
"2006-01-02",
time.RFC1123,
time.RFC1123Z,
dateTimeLayout,
time.UnixDate,
time.ANSIC,
time.RubyDate,
"Mon, 02 Jan 2006 15:04:05 GMT-0700 (MST)",
"Mon, 02 Jan 2006 15:04:05 -0700 (MST)",
"2006",
"2006-01",
"2006T15:04",
"2006-01T15:04",
"2006-01-02T15:04",
"2006T15:04:05",
"2006-01T15:04:05",
"2006T15:04:05.000",
"2006-01T15:04:05.000",
"2006T15:04Z0700",
"2006-01T15:04Z0700",
"2006-01-02T15:04Z0700",
"2006T15:04:05Z0700",
"2006-01T15:04:05Z0700",
"2006T15:04:05.000Z0700",
"2006-01T15:04:05.000Z0700",
}
)
func dateParse(date string) (time.Time, bool) {
var t time.Time
var err error
for _, layout := range dateLayoutList {
t, err = parseDate(layout, date, time.UTC)
if err == nil {
break
}
}
unix := timeToMsec(t)
return t, err == nil && unix >= -8640000000000000 && unix <= 8640000000000000
}
func (r *Runtime) newDateObject(t time.Time, isSet bool) *Object {
v := &Object{runtime: r}
d := &dateObject{}
v.self = d
d.val = v
d.class = classDate
d.prototype = r.global.DatePrototype
d.extensible = true
d.init()
d.time = t.In(time.Local)
d.isSet = isSet
return v
}
func dateFormat(t time.Time) string {
return t.Local().Format(dateTimeLayout)
}
func (d *dateObject) toPrimitive() Value {
return d.toPrimitiveString()
}
func (d *dateObject) export() interface{} {
if d.isSet {
return d.time
}
return nil
}

860
vendor/github.com/dop251/goja/date_parser.go generated vendored Normal file
View file

@ -0,0 +1,860 @@
package goja
// This is a slightly modified version of the standard Go parser to make it more compatible with ECMAScript 5.1
// Changes:
// - 6-digit extended years are supported in place of long year (2006) in the form of +123456
// - Timezone formats tolerate colons, e.g. -0700 will parse -07:00
// - Short week day will also parse long week day
// - Timezone in brackets, "(MST)", will match any string in brackets (e.g. "(GMT Standard Time)")
// - If offset is not set and timezone name is unknown, an error is returned
// - If offset and timezone name are both set the offset takes precedence and the resulting Location will be FixedZone("", offset)
// Original copyright message:
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import (
"errors"
"time"
)
const (
_ = iota
stdLongMonth = iota + stdNeedDate // "January"
stdMonth // "Jan"
stdNumMonth // "1"
stdZeroMonth // "01"
stdLongWeekDay // "Monday"
stdWeekDay // "Mon"
stdDay // "2"
stdUnderDay // "_2"
stdZeroDay // "02"
stdHour = iota + stdNeedClock // "15"
stdHour12 // "3"
stdZeroHour12 // "03"
stdMinute // "4"
stdZeroMinute // "04"
stdSecond // "5"
stdZeroSecond // "05"
stdLongYear = iota + stdNeedDate // "2006"
stdYear // "06"
stdPM = iota + stdNeedClock // "PM"
stdpm // "pm"
stdTZ = iota // "MST"
stdBracketTZ // "(MST)"
stdISO8601TZ // "Z0700" // prints Z for UTC
stdISO8601SecondsTZ // "Z070000"
stdISO8601ShortTZ // "Z07"
stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
stdISO8601ColonSecondsTZ // "Z07:00:00"
stdNumTZ // "-0700" // always numeric
stdNumSecondsTz // "-070000"
stdNumShortTZ // "-07" // always numeric
stdNumColonTZ // "-07:00" // always numeric
stdNumColonSecondsTZ // "-07:00:00"
stdFracSecond0 // ".0", ".00", ... , trailing zeros included
stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
stdNeedDate = 1 << 8 // need month, day, year
stdNeedClock = 2 << 8 // need hour, minute, second
stdArgShift = 16 // extra argument in high bits, above low stdArgShift
stdMask = 1<<stdArgShift - 1 // mask out argument
)
var errBad = errors.New("bad value for field") // placeholder not passed to user
func parseDate(layout, value string, defaultLocation *time.Location) (time.Time, error) {
alayout, avalue := layout, value
rangeErrString := "" // set if a value is out of range
amSet := false // do we need to subtract 12 from the hour for midnight?
pmSet := false // do we need to add 12 to the hour?
// Time being constructed.
var (
year int
month int = 1 // January
day int = 1
hour int
min int
sec int
nsec int
z *time.Location
zoneOffset int = -1
zoneName string
)
// Each iteration processes one std value.
for {
var err error
prefix, std, suffix := nextStdChunk(layout)
stdstr := layout[len(prefix) : len(layout)-len(suffix)]
value, err = skip(value, prefix)
if err != nil {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: prefix, ValueElem: value}
}
if std == 0 {
if len(value) != 0 {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": extra text: " + value}
}
break
}
layout = suffix
var p string
switch std & stdMask {
case stdYear:
if len(value) < 2 {
err = errBad
break
}
p, value = value[0:2], value[2:]
year, err = atoi(p)
if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
year += 1900
} else {
year += 2000
}
case stdLongYear:
if len(value) >= 7 && (value[0] == '-' || value[0] == '+') { // extended year
neg := value[0] == '-'
p, value = value[1:7], value[7:]
year, err = atoi(p)
if neg {
year = -year
}
} else {
if len(value) < 4 || !isDigit(value, 0) {
err = errBad
break
}
p, value = value[0:4], value[4:]
year, err = atoi(p)
}
case stdMonth:
month, value, err = lookup(shortMonthNames, value)
month++
case stdLongMonth:
month, value, err = lookup(longMonthNames, value)
month++
case stdNumMonth, stdZeroMonth:
month, value, err = getnum(value, std == stdZeroMonth)
if month <= 0 || 12 < month {
rangeErrString = "month"
}
case stdWeekDay:
// Ignore weekday except for error checking.
_, value, err = lookup(longDayNames, value)
if err != nil {
_, value, err = lookup(shortDayNames, value)
}
case stdLongWeekDay:
_, value, err = lookup(longDayNames, value)
case stdDay, stdUnderDay, stdZeroDay:
if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
value = value[1:]
}
day, value, err = getnum(value, std == stdZeroDay)
if day < 0 {
// Note that we allow any one- or two-digit day here.
rangeErrString = "day"
}
case stdHour:
hour, value, err = getnum(value, false)
if hour < 0 || 24 <= hour {
rangeErrString = "hour"
}
case stdHour12, stdZeroHour12:
hour, value, err = getnum(value, std == stdZeroHour12)
if hour < 0 || 12 < hour {
rangeErrString = "hour"
}
case stdMinute, stdZeroMinute:
min, value, err = getnum(value, std == stdZeroMinute)
if min < 0 || 60 <= min {
rangeErrString = "minute"
}
case stdSecond, stdZeroSecond:
sec, value, err = getnum(value, std == stdZeroSecond)
if sec < 0 || 60 <= sec {
rangeErrString = "second"
break
}
// Special case: do we have a fractional second but no
// fractional second in the format?
if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
_, std, _ = nextStdChunk(layout)
std &= stdMask
if std == stdFracSecond0 || std == stdFracSecond9 {
// Fractional second in the layout; proceed normally
break
}
// No fractional second in the layout but we have one in the input.
n := 2
for ; n < len(value) && isDigit(value, n); n++ {
}
nsec, rangeErrString, err = parseNanoseconds(value, n)
value = value[n:]
}
case stdPM:
if len(value) < 2 {
err = errBad
break
}
p, value = value[0:2], value[2:]
switch p {
case "PM":
pmSet = true
case "AM":
amSet = true
default:
err = errBad
}
case stdpm:
if len(value) < 2 {
err = errBad
break
}
p, value = value[0:2], value[2:]
switch p {
case "pm":
pmSet = true
case "am":
amSet = true
default:
err = errBad
}
case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ ||
std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) && len(value) >= 1 && value[0] == 'Z' {
value = value[1:]
z = time.UTC
break
}
var sign, hour, min, seconds string
if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdNumTZ || std == stdISO8601TZ {
if len(value) < 4 {
err = errBad
break
}
if value[3] != ':' {
if std == stdNumColonTZ || std == stdISO8601ColonTZ || len(value) < 5 {
err = errBad
break
}
sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
} else {
if len(value) < 6 {
err = errBad
break
}
sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
}
} else if std == stdNumShortTZ || std == stdISO8601ShortTZ {
if len(value) < 3 {
err = errBad
break
}
sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
} else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
if len(value) < 7 {
err = errBad
break
}
if value[3] != ':' || value[6] != ':' {
if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || len(value) < 7 {
err = errBad
break
}
sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
} else {
if len(value) < 9 {
err = errBad
break
}
sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
}
}
var hr, mm, ss int
hr, err = atoi(hour)
if err == nil {
mm, err = atoi(min)
}
if err == nil {
ss, err = atoi(seconds)
}
zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds
switch sign[0] {
case '+':
case '-':
zoneOffset = -zoneOffset
default:
err = errBad
}
case stdTZ:
// Does it look like a time zone?
if len(value) >= 3 && value[0:3] == "UTC" {
z = time.UTC
value = value[3:]
break
}
n, ok := parseTimeZone(value)
if !ok {
err = errBad
break
}
zoneName, value = value[:n], value[n:]
case stdBracketTZ:
if len(value) < 3 || value[0] != '(' {
err = errBad
break
}
i := 1
for ; ; i++ {
if i >= len(value) {
err = errBad
break
}
if value[i] == ')' {
zoneName, value = value[1:i], value[i+1:]
break
}
}
case stdFracSecond0:
// stdFracSecond0 requires the exact number of digits as specified in
// the layout.
ndigit := 1 + (std >> stdArgShift)
if len(value) < ndigit {
err = errBad
break
}
nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
value = value[ndigit:]
case stdFracSecond9:
if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
// Fractional second omitted.
break
}
// Take any number of digits, even more than asked for,
// because it is what the stdSecond case would do.
i := 0
for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
i++
}
nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
value = value[1+i:]
}
if rangeErrString != "" {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value, Message: ": " + rangeErrString + " out of range"}
}
if err != nil {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value}
}
}
if pmSet && hour < 12 {
hour += 12
} else if amSet && hour == 12 {
hour = 0
}
// Validate the day of the month.
if day < 1 || day > daysIn(time.Month(month), year) {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": day out of range"}
}
if z == nil {
if zoneOffset == -1 {
if zoneName != "" {
if z1, err := time.LoadLocation(zoneName); err == nil {
z = z1
} else {
return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": unknown timezone"}
}
} else {
z = defaultLocation
}
} else if zoneOffset == 0 {
z = time.UTC
} else {
z = time.FixedZone("", zoneOffset)
}
}
return time.Date(year, time.Month(month), day, hour, min, sec, nsec, z), nil
}
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
func signedLeadingInt(s string) (x int64, rem string, err error) {
neg := false
if s != "" && (s[0] == '-' || s[0] == '+') {
neg = s[0] == '-'
s = s[1:]
}
x, rem, err = leadingInt(s)
if err != nil {
return
}
if neg {
x = -x
}
return
}
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x > (1<<63-1)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
if x < 0 {
// overflow
return 0, "", errLeadingInt
}
}
return x, s[i:], nil
}
// nextStdChunk finds the first occurrence of a std string in
// layout and returns the text before, the std string, and the text after.
func nextStdChunk(layout string) (prefix string, std int, suffix string) {
for i := 0; i < len(layout); i++ {
switch c := int(layout[i]); c {
case 'J': // January, Jan
if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
if len(layout) >= i+7 && layout[i:i+7] == "January" {
return layout[0:i], stdLongMonth, layout[i+7:]
}
if !startsWithLowerCase(layout[i+3:]) {
return layout[0:i], stdMonth, layout[i+3:]
}
}
case 'M': // Monday, Mon, MST
if len(layout) >= i+3 {
if layout[i:i+3] == "Mon" {
if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
return layout[0:i], stdLongWeekDay, layout[i+6:]
}
if !startsWithLowerCase(layout[i+3:]) {
return layout[0:i], stdWeekDay, layout[i+3:]
}
}
if layout[i:i+3] == "MST" {
return layout[0:i], stdTZ, layout[i+3:]
}
}
case '0': // 01, 02, 03, 04, 05, 06
if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
}
case '1': // 15, 1
if len(layout) >= i+2 && layout[i+1] == '5' {
return layout[0:i], stdHour, layout[i+2:]
}
return layout[0:i], stdNumMonth, layout[i+1:]
case '2': // 2006, 2
if len(layout) >= i+4 && layout[i:i+4] == "2006" {
return layout[0:i], stdLongYear, layout[i+4:]
}
return layout[0:i], stdDay, layout[i+1:]
case '_': // _2, _2006
if len(layout) >= i+2 && layout[i+1] == '2' {
//_2006 is really a literal _, followed by stdLongYear
if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
return layout[0 : i+1], stdLongYear, layout[i+5:]
}
return layout[0:i], stdUnderDay, layout[i+2:]
}
case '3':
return layout[0:i], stdHour12, layout[i+1:]
case '4':
return layout[0:i], stdMinute, layout[i+1:]
case '5':
return layout[0:i], stdSecond, layout[i+1:]
case 'P': // PM
if len(layout) >= i+2 && layout[i+1] == 'M' {
return layout[0:i], stdPM, layout[i+2:]
}
case 'p': // pm
if len(layout) >= i+2 && layout[i+1] == 'm' {
return layout[0:i], stdpm, layout[i+2:]
}
case '-': // -070000, -07:00:00, -0700, -07:00, -07
if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
return layout[0:i], stdNumSecondsTz, layout[i+7:]
}
if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
}
if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
return layout[0:i], stdNumTZ, layout[i+5:]
}
if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
return layout[0:i], stdNumColonTZ, layout[i+6:]
}
if len(layout) >= i+3 && layout[i:i+3] == "-07" {
return layout[0:i], stdNumShortTZ, layout[i+3:]
}
case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
}
if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
}
if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
return layout[0:i], stdISO8601TZ, layout[i+5:]
}
if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
}
if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
}
case '.': // .000 or .999 - repeated digits for fractional seconds.
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
ch := layout[i+1]
j := i + 1
for j < len(layout) && layout[j] == ch {
j++
}
// String of digits must end here - only fractional second is all digits.
if !isDigit(layout, j) {
std := stdFracSecond0
if layout[i+1] == '9' {
std = stdFracSecond9
}
std |= (j - (i + 1)) << stdArgShift
return layout[0:i], std, layout[j:]
}
}
case '(':
if len(layout) >= i+5 && layout[i:i+5] == "(MST)" {
return layout[0:i], stdBracketTZ, layout[i+5:]
}
}
}
return layout, 0, ""
}
var longDayNames = []string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
var shortDayNames = []string{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
}
var shortMonthNames = []string{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
}
var longMonthNames = []string{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
}
// isDigit reports whether s[i] is in range and is a decimal digit.
func isDigit(s string, i int) bool {
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}
// getnum parses s[0:1] or s[0:2] (fixed forces the latter)
// as a decimal integer and returns the integer and the
// remainder of the string.
func getnum(s string, fixed bool) (int, string, error) {
if !isDigit(s, 0) {
return 0, s, errBad
}
if !isDigit(s, 1) {
if fixed {
return 0, s, errBad
}
return int(s[0] - '0'), s[1:], nil
}
return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
}
func cutspace(s string) string {
for len(s) > 0 && s[0] == ' ' {
s = s[1:]
}
return s
}
// skip removes the given prefix from value,
// treating runs of space characters as equivalent.
func skip(value, prefix string) (string, error) {
for len(prefix) > 0 {
if prefix[0] == ' ' {
if len(value) > 0 && value[0] != ' ' {
return value, errBad
}
prefix = cutspace(prefix)
value = cutspace(value)
continue
}
if len(value) == 0 || value[0] != prefix[0] {
return value, errBad
}
prefix = prefix[1:]
value = value[1:]
}
return value, nil
}
// Never printed, just needs to be non-nil for return by atoi.
var atoiError = errors.New("time: invalid number")
// Duplicates functionality in strconv, but avoids dependency.
func atoi(s string) (x int, err error) {
q, rem, err := signedLeadingInt(s)
x = int(q)
if err != nil || rem != "" {
return 0, atoiError
}
return x, nil
}
// match reports whether s1 and s2 match ignoring case.
// It is assumed s1 and s2 are the same length.
func match(s1, s2 string) bool {
for i := 0; i < len(s1); i++ {
c1 := s1[i]
c2 := s2[i]
if c1 != c2 {
// Switch to lower-case; 'a'-'A' is known to be a single bit.
c1 |= 'a' - 'A'
c2 |= 'a' - 'A'
if c1 != c2 || c1 < 'a' || c1 > 'z' {
return false
}
}
}
return true
}
func lookup(tab []string, val string) (int, string, error) {
for i, v := range tab {
if len(val) >= len(v) && match(val[0:len(v)], v) {
return i, val[len(v):], nil
}
}
return -1, val, errBad
}
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins. There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
func daysIn(m time.Month, year int) int {
if m == time.February && isLeap(year) {
return 29
}
return int(daysBefore[m] - daysBefore[m-1])
}
// parseTimeZone parses a time zone string and returns its length. Time zones
// are human-generated and unpredictable. We can't do precise error checking.
// On the other hand, for a correct parse there must be a time zone at the
// beginning of the string, so it's almost always true that there's one
// there. We look at the beginning of the string for a run of upper-case letters.
// If there are more than 5, it's an error.
// If there are 4 or 5 and the last is a T, it's a time zone.
// If there are 3, it's a time zone.
// Otherwise, other than special cases, it's not a time zone.
// GMT is special because it can have an hour offset.
func parseTimeZone(value string) (length int, ok bool) {
if len(value) < 3 {
return 0, false
}
// Special case 1: ChST and MeST are the only zones with a lower-case letter.
if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
return 4, true
}
// Special case 2: GMT may have an hour offset; treat it specially.
if value[:3] == "GMT" {
length = parseGMT(value)
return length, true
}
// Special Case 3: Some time zones are not named, but have +/-00 format
if value[0] == '+' || value[0] == '-' {
length = parseSignedOffset(value)
return length, true
}
// How many upper-case letters are there? Need at least three, at most five.
var nUpper int
for nUpper = 0; nUpper < 6; nUpper++ {
if nUpper >= len(value) {
break
}
if c := value[nUpper]; c < 'A' || 'Z' < c {
break
}
}
switch nUpper {
case 0, 1, 2, 6:
return 0, false
case 5: // Must end in T to match.
if value[4] == 'T' {
return 5, true
}
case 4:
// Must end in T, except one special case.
if value[3] == 'T' || value[:4] == "WITA" {
return 4, true
}
case 3:
return 3, true
}
return 0, false
}
// parseGMT parses a GMT time zone. The input string is known to start "GMT".
// The function checks whether that is followed by a sign and a number in the
// range -14 through 12 excluding zero.
func parseGMT(value string) int {
value = value[3:]
if len(value) == 0 {
return 3
}
return 3 + parseSignedOffset(value)
}
// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
// The function checks for a signed number in the range -14 through +12 excluding zero.
// Returns length of the found offset string or 0 otherwise
func parseSignedOffset(value string) int {
sign := value[0]
if sign != '-' && sign != '+' {
return 0
}
x, rem, err := leadingInt(value[1:])
if err != nil {
return 0
}
if sign == '-' {
x = -x
}
if x == 0 || x < -14 || 12 < x {
return 0
}
return len(value) - len(rem)
}
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
if value[0] != '.' {
err = errBad
return
}
if ns, err = atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 || 1e9 <= ns {
rangeErrString = "fractional second"
return
}
// We need nanoseconds, which means scaling by the number
// of missing digits in the format, maximum length 10. If it's
// longer than 10, we won't scale.
scaleDigits := 10 - nbytes
for i := 0; i < scaleDigits; i++ {
ns *= 10
}
return
}
// std0x records the std values for "01", "02", ..., "06".
var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
// startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
// Its purpose is to prevent matching strings like "Month" when looking for "Mon".
func startsWithLowerCase(str string) bool {
if len(str) == 0 {
return false
}
c := str[0]
return 'a' <= c && c <= 'z'
}

290
vendor/github.com/dop251/goja/dtoa.go generated vendored Normal file
View file

@ -0,0 +1,290 @@
package goja
// Ported from Rhino (https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/DToA.java)
import (
"bytes"
"fmt"
"math"
"math/big"
"strconv"
)
const (
frac_mask = 0xfffff
exp_shift = 20
exp_msk1 = 0x100000
exp_shiftL = 52
exp_mask_shifted = 0x7ff
frac_maskL = 0xfffffffffffff
exp_msk1L = 0x10000000000000
exp_shift1 = 20
exp_mask = 0x7ff00000
bias = 1023
p = 53
bndry_mask = 0xfffff
log2P = 1
digits = "0123456789abcdefghijklmnopqrstuvwxyz"
)
func lo0bits(x uint32) (k uint32) {
if (x & 7) != 0 {
if (x & 1) != 0 {
return 0
}
if (x & 2) != 0 {
return 1
}
return 2
}
if (x & 0xffff) == 0 {
k = 16
x >>= 16
}
if (x & 0xff) == 0 {
k += 8
x >>= 8
}
if (x & 0xf) == 0 {
k += 4
x >>= 4
}
if (x & 0x3) == 0 {
k += 2
x >>= 2
}
if (x & 1) == 0 {
k++
x >>= 1
if (x & 1) == 0 {
return 32
}
}
return
}
func hi0bits(x uint32) (k uint32) {
if (x & 0xffff0000) == 0 {
k = 16
x <<= 16
}
if (x & 0xff000000) == 0 {
k += 8
x <<= 8
}
if (x & 0xf0000000) == 0 {
k += 4
x <<= 4
}
if (x & 0xc0000000) == 0 {
k += 2
x <<= 2
}
if (x & 0x80000000) == 0 {
k++
if (x & 0x40000000) == 0 {
return 32
}
}
return
}
func stuffBits(bits []byte, offset int, val uint32) {
bits[offset] = byte(val >> 24)
bits[offset+1] = byte(val >> 16)
bits[offset+2] = byte(val >> 8)
bits[offset+3] = byte(val)
}
func d2b(d float64) (b *big.Int, e int32, bits uint32) {
dBits := math.Float64bits(d)
d0 := uint32(dBits >> 32)
d1 := uint32(dBits)
z := d0 & frac_mask
d0 &= 0x7fffffff /* clear sign bit, which we ignore */
var de, k, i uint32
var dbl_bits []byte
if de = (d0 >> exp_shift); de != 0 {
z |= exp_msk1
}
y := d1
if y != 0 {
dbl_bits = make([]byte, 8)
k = lo0bits(y)
y >>= k
if k != 0 {
stuffBits(dbl_bits, 4, y|z<<(32-k))
z >>= k
} else {
stuffBits(dbl_bits, 4, y)
}
stuffBits(dbl_bits, 0, z)
if z != 0 {
i = 2
} else {
i = 1
}
} else {
dbl_bits = make([]byte, 4)
k = lo0bits(z)
z >>= k
stuffBits(dbl_bits, 0, z)
k += 32
i = 1
}
if de != 0 {
e = int32(de - bias - (p - 1) + k)
bits = p - k
} else {
e = int32(de - bias - (p - 1) + 1 + k)
bits = 32*i - hi0bits(z)
}
b = (&big.Int{}).SetBytes(dbl_bits)
return
}
func dtobasestr(num float64, radix int) string {
var negative bool
if num < 0 {
num = -num
negative = true
}
dfloor := math.Floor(num)
ldfloor := int64(dfloor)
var intDigits string
if dfloor == float64(ldfloor) {
if negative {
ldfloor = -ldfloor
}
intDigits = strconv.FormatInt(ldfloor, radix)
} else {
floorBits := math.Float64bits(num)
exp := int(floorBits>>exp_shiftL) & exp_mask_shifted
var mantissa int64
if exp == 0 {
mantissa = int64((floorBits & frac_maskL) << 1)
} else {
mantissa = int64((floorBits & frac_maskL) | exp_msk1L)
}
if negative {
mantissa = -mantissa
}
exp -= 1075
x := big.NewInt(mantissa)
if exp > 0 {
x.Lsh(x, uint(exp))
} else if exp < 0 {
x.Rsh(x, uint(-exp))
}
intDigits = x.Text(radix)
}
if num == dfloor {
// No fraction part
return intDigits
} else {
/* We have a fraction. */
var buffer bytes.Buffer
buffer.WriteString(intDigits)
buffer.WriteByte('.')
df := num - dfloor
dBits := math.Float64bits(num)
word0 := uint32(dBits >> 32)
word1 := uint32(dBits)
b, e, _ := d2b(df)
// JS_ASSERT(e < 0);
/* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */
s2 := -int32((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
if s2 == 0 {
s2 = -1
}
s2 += bias + p
/* 1/2^s2 = (nextDouble(d) - d)/2 */
// JS_ASSERT(-s2 < e);
if -s2 >= e {
panic(fmt.Errorf("-s2 >= e: %d, %d", -s2, e))
}
mlo := big.NewInt(1)
mhi := mlo
if (word1 == 0) && ((word0 & bndry_mask) == 0) && ((word0 & (exp_mask & (exp_mask << 1))) != 0) {
/* The special case. Here we want to be within a quarter of the last input
significant digit instead of one half of it when the output string's value is less than d. */
s2 += log2P
mhi = big.NewInt(1 << log2P)
}
b.Lsh(b, uint(e+s2))
s := big.NewInt(1)
s.Lsh(s, uint(s2))
/* At this point we have the following:
* s = 2^s2;
* 1 > df = b/2^s2 > 0;
* (d - prevDouble(d))/2 = mlo/2^s2;
* (nextDouble(d) - d)/2 = mhi/2^s2. */
bigBase := big.NewInt(int64(radix))
done := false
m := &big.Int{}
delta := &big.Int{}
for !done {
b.Mul(b, bigBase)
b.DivMod(b, s, m)
digit := byte(b.Int64())
b, m = m, b
mlo.Mul(mlo, bigBase)
if mlo != mhi {
mhi.Mul(mhi, bigBase)
}
/* Do we yet have the shortest string that will round to d? */
j := b.Cmp(mlo)
/* j is b/2^s2 compared with mlo/2^s2. */
delta.Sub(s, mhi)
var j1 int
if delta.Sign() <= 0 {
j1 = 1
} else {
j1 = b.Cmp(delta)
}
/* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
if j1 == 0 && (word1&1) == 0 {
if j > 0 {
digit++
}
done = true
} else if j < 0 || (j == 0 && ((word1 & 1) == 0)) {
if j1 > 0 {
/* Either dig or dig+1 would work here as the least significant digit.
Use whichever would produce an output value closer to d. */
b.Lsh(b, 1)
j1 = b.Cmp(s)
if j1 > 0 { /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output such as 3.5 in base 3. */
digit++
}
}
done = true
} else if j1 > 0 {
digit++
done = true
}
// JS_ASSERT(digit < (uint32)base);
buffer.WriteByte(digits[digit])
}
return buffer.String()
}
}

240
vendor/github.com/dop251/goja/func.go generated vendored Normal file
View file

@ -0,0 +1,240 @@
package goja
import "reflect"
type baseFuncObject struct {
baseObject
nameProp, lenProp valueProperty
}
type funcObject struct {
baseFuncObject
stash *stash
prg *Program
src string
}
type nativeFuncObject struct {
baseFuncObject
f func(FunctionCall) Value
construct func(args []Value) *Object
}
type boundFuncObject struct {
nativeFuncObject
}
func (f *nativeFuncObject) export() interface{} {
return f.f
}
func (f *nativeFuncObject) exportType() reflect.Type {
return reflect.TypeOf(f.f)
}
func (f *funcObject) getPropStr(name string) Value {
switch name {
case "prototype":
if _, exists := f.values["prototype"]; !exists {
return f.addPrototype()
}
}
return f.baseObject.getPropStr(name)
}
func (f *funcObject) addPrototype() Value {
proto := f.val.runtime.NewObject()
proto.self._putProp("constructor", f.val, true, false, true)
return f._putProp("prototype", proto, true, false, false)
}
func (f *funcObject) getProp(n Value) Value {
return f.getPropStr(n.String())
}
func (f *funcObject) hasOwnProperty(n Value) bool {
if r := f.baseObject.hasOwnProperty(n); r {
return true
}
name := n.String()
if name == "prototype" {
return true
}
return false
}
func (f *funcObject) hasOwnPropertyStr(name string) bool {
if r := f.baseObject.hasOwnPropertyStr(name); r {
return true
}
if name == "prototype" {
return true
}
return false
}
func (f *funcObject) construct(args []Value) *Object {
proto := f.getStr("prototype")
var protoObj *Object
if p, ok := proto.(*Object); ok {
protoObj = p
} else {
protoObj = f.val.runtime.global.ObjectPrototype
}
obj := f.val.runtime.newBaseObject(protoObj, classObject).val
ret := f.Call(FunctionCall{
This: obj,
Arguments: args,
})
if ret, ok := ret.(*Object); ok {
return ret
}
return obj
}
func (f *funcObject) Call(call FunctionCall) Value {
vm := f.val.runtime.vm
pc := vm.pc
vm.stack.expand(vm.sp + len(call.Arguments) + 1)
vm.stack[vm.sp] = f.val
vm.sp++
if call.This != nil {
vm.stack[vm.sp] = call.This
} else {
vm.stack[vm.sp] = _undefined
}
vm.sp++
for _, arg := range call.Arguments {
if arg != nil {
vm.stack[vm.sp] = arg
} else {
vm.stack[vm.sp] = _undefined
}
vm.sp++
}
vm.pc = -1
vm.pushCtx()
vm.args = len(call.Arguments)
vm.prg = f.prg
vm.stash = f.stash
vm.pc = 0
vm.run()
vm.pc = pc
vm.halt = false
return vm.pop()
}
func (f *funcObject) export() interface{} {
return f.Call
}
func (f *funcObject) exportType() reflect.Type {
return reflect.TypeOf(f.Call)
}
func (f *funcObject) assertCallable() (func(FunctionCall) Value, bool) {
return f.Call, true
}
func (f *baseFuncObject) init(name string, length int) {
f.baseObject.init()
f.nameProp.configurable = true
f.nameProp.value = newStringValue(name)
f._put("name", &f.nameProp)
f.lenProp.configurable = true
f.lenProp.value = valueInt(length)
f._put("length", &f.lenProp)
}
func (f *baseFuncObject) hasInstance(v Value) bool {
if v, ok := v.(*Object); ok {
o := f.val.self.getStr("prototype")
if o1, ok := o.(*Object); ok {
for {
v = v.self.proto()
if v == nil {
return false
}
if o1 == v {
return true
}
}
} else {
f.val.runtime.typeErrorResult(true, "prototype is not an object")
}
}
return false
}
func (f *nativeFuncObject) defaultConstruct(ccall func(ConstructorCall) *Object, args []Value) *Object {
proto := f.getStr("prototype")
var protoObj *Object
if p, ok := proto.(*Object); ok {
protoObj = p
} else {
protoObj = f.val.runtime.global.ObjectPrototype
}
obj := f.val.runtime.newBaseObject(protoObj, classObject).val
ret := ccall(ConstructorCall{
This: obj,
Arguments: args,
})
if ret != nil {
return ret
}
return obj
}
func (f *nativeFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
if f.f != nil {
return f.f, true
}
return nil, false
}
func (f *boundFuncObject) getProp(n Value) Value {
return f.getPropStr(n.String())
}
func (f *boundFuncObject) getPropStr(name string) Value {
if name == "caller" || name == "arguments" {
//f.runtime.typeErrorResult(true, "'caller' and 'arguments' are restricted function properties and cannot be accessed in this context.")
return f.val.runtime.global.throwerProperty
}
return f.nativeFuncObject.getPropStr(name)
}
func (f *boundFuncObject) delete(n Value, throw bool) bool {
return f.deleteStr(n.String(), throw)
}
func (f *boundFuncObject) deleteStr(name string, throw bool) bool {
if name == "caller" || name == "arguments" {
return true
}
return f.nativeFuncObject.deleteStr(name, throw)
}
func (f *boundFuncObject) putStr(name string, val Value, throw bool) {
if name == "caller" || name == "arguments" {
f.val.runtime.typeErrorResult(true, "'caller' and 'arguments' are restricted function properties and cannot be accessed in this context.")
}
f.nativeFuncObject.putStr(name, val, throw)
}
func (f *boundFuncObject) put(n Value, val Value, throw bool) {
f.putStr(n.String(), val, throw)
}

97
vendor/github.com/dop251/goja/ipow.go generated vendored Normal file
View file

@ -0,0 +1,97 @@
package goja
// ported from https://gist.github.com/orlp/3551590
var highest_bit_set = [256]byte{
0, 1, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 255, // anything past 63 is a guaranteed overflow with base > 1
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}
func ipow(base, exp int64) (result int64) {
result = 1
switch highest_bit_set[byte(exp)] {
case 255: // we use 255 as an overflow marker and return 0 on overflow/underflow
if base == 1 {
return 1
}
if base == -1 {
return 1 - 2*(exp&1)
}
return 0
case 6:
if exp&1 != 0 {
result *= base
}
exp >>= 1
base *= base
fallthrough
case 5:
if exp&1 != 0 {
result *= base
}
exp >>= 1
base *= base
fallthrough
case 4:
if exp&1 != 0 {
result *= base
}
exp >>= 1
base *= base
fallthrough
case 3:
if exp&1 != 0 {
result *= base
}
exp >>= 1
base *= base
fallthrough
case 2:
if exp&1 != 0 {
result *= base
}
exp >>= 1
base *= base
fallthrough
case 1:
if exp&1 != 0 {
result *= base
}
fallthrough
default:
return result
}
}

625
vendor/github.com/dop251/goja/object.go generated vendored Normal file
View file

@ -0,0 +1,625 @@
package goja
import "reflect"
const (
classObject = "Object"
classArray = "Array"
classFunction = "Function"
classNumber = "Number"
classString = "String"
classBoolean = "Boolean"
classError = "Error"
classRegExp = "RegExp"
classDate = "Date"
)
type Object struct {
runtime *Runtime
self objectImpl
}
type iterNextFunc func() (propIterItem, iterNextFunc)
type propertyDescr struct {
Value Value
Writable, Configurable, Enumerable Flag
Getter, Setter Value
}
type objectImpl interface {
sortable
className() string
get(Value) Value
getProp(Value) Value
getPropStr(string) Value
getStr(string) Value
getOwnProp(string) Value
put(Value, Value, bool)
putStr(string, Value, bool)
hasProperty(Value) bool
hasPropertyStr(string) bool
hasOwnProperty(Value) bool
hasOwnPropertyStr(string) bool
_putProp(name string, value Value, writable, enumerable, configurable bool) Value
defineOwnProperty(name Value, descr propertyDescr, throw bool) bool
toPrimitiveNumber() Value
toPrimitiveString() Value
toPrimitive() Value
assertCallable() (call func(FunctionCall) Value, ok bool)
deleteStr(name string, throw bool) bool
delete(name Value, throw bool) bool
proto() *Object
hasInstance(v Value) bool
isExtensible() bool
preventExtensions()
enumerate(all, recusrive bool) iterNextFunc
_enumerate(recursive bool) iterNextFunc
export() interface{}
exportType() reflect.Type
equal(objectImpl) bool
}
type baseObject struct {
class string
val *Object
prototype *Object
extensible bool
values map[string]Value
propNames []string
}
type primitiveValueObject struct {
baseObject
pValue Value
}
func (o *primitiveValueObject) export() interface{} {
return o.pValue.Export()
}
func (o *primitiveValueObject) exportType() reflect.Type {
return o.pValue.ExportType()
}
type FunctionCall struct {
This Value
Arguments []Value
}
type ConstructorCall struct {
This *Object
Arguments []Value
}
func (f FunctionCall) Argument(idx int) Value {
if idx < len(f.Arguments) {
return f.Arguments[idx]
}
return _undefined
}
func (f ConstructorCall) Argument(idx int) Value {
if idx < len(f.Arguments) {
return f.Arguments[idx]
}
return _undefined
}
func (o *baseObject) init() {
o.values = make(map[string]Value)
}
func (o *baseObject) className() string {
return o.class
}
func (o *baseObject) getPropStr(name string) Value {
if val := o.getOwnProp(name); val != nil {
return val
}
if o.prototype != nil {
return o.prototype.self.getPropStr(name)
}
return nil
}
func (o *baseObject) getProp(n Value) Value {
return o.val.self.getPropStr(n.String())
}
func (o *baseObject) hasProperty(n Value) bool {
return o.val.self.getProp(n) != nil
}
func (o *baseObject) hasPropertyStr(name string) bool {
return o.val.self.getPropStr(name) != nil
}
func (o *baseObject) _getStr(name string) Value {
p := o.getOwnProp(name)
if p == nil && o.prototype != nil {
p = o.prototype.self.getPropStr(name)
}
if p, ok := p.(*valueProperty); ok {
return p.get(o.val)
}
return p
}
func (o *baseObject) getStr(name string) Value {
p := o.val.self.getPropStr(name)
if p, ok := p.(*valueProperty); ok {
return p.get(o.val)
}
return p
}
func (o *baseObject) get(n Value) Value {
return o.getStr(n.String())
}
func (o *baseObject) checkDeleteProp(name string, prop *valueProperty, throw bool) bool {
if !prop.configurable {
o.val.runtime.typeErrorResult(throw, "Cannot delete property '%s' of %s", name, o.val.ToString())
return false
}
return true
}
func (o *baseObject) checkDelete(name string, val Value, throw bool) bool {
if val, ok := val.(*valueProperty); ok {
return o.checkDeleteProp(name, val, throw)
}
return true
}
func (o *baseObject) _delete(name string) {
delete(o.values, name)
for i, n := range o.propNames {
if n == name {
copy(o.propNames[i:], o.propNames[i+1:])
o.propNames = o.propNames[:len(o.propNames)-1]
break
}
}
}
func (o *baseObject) deleteStr(name string, throw bool) bool {
if val, exists := o.values[name]; exists {
if !o.checkDelete(name, val, throw) {
return false
}
o._delete(name)
return true
}
return true
}
func (o *baseObject) delete(n Value, throw bool) bool {
return o.deleteStr(n.String(), throw)
}
func (o *baseObject) put(n Value, val Value, throw bool) {
o.putStr(n.String(), val, throw)
}
func (o *baseObject) getOwnProp(name string) Value {
v := o.values[name]
if v == nil && name == "__proto" {
return o.prototype
}
return v
}
func (o *baseObject) putStr(name string, val Value, throw bool) {
if v, exists := o.values[name]; exists {
if prop, ok := v.(*valueProperty); ok {
if !prop.isWritable() {
o.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%s'", name)
return
}
prop.set(o.val, val)
return
}
o.values[name] = val
return
}
if name == "__proto__" {
if !o.extensible {
o.val.runtime.typeErrorResult(throw, "%s is not extensible", o.val)
return
}
if val == _undefined || val == _null {
o.prototype = nil
return
} else {
if val, ok := val.(*Object); ok {
o.prototype = val
}
}
return
}
var pprop Value
if proto := o.prototype; proto != nil {
pprop = proto.self.getPropStr(name)
}
if pprop != nil {
if prop, ok := pprop.(*valueProperty); ok {
if !prop.isWritable() {
o.val.runtime.typeErrorResult(throw)
return
}
if prop.accessor {
prop.set(o.val, val)
return
}
}
} else {
if !o.extensible {
o.val.runtime.typeErrorResult(throw)
return
}
}
o.values[name] = val
o.propNames = append(o.propNames, name)
}
func (o *baseObject) hasOwnProperty(n Value) bool {
v := o.values[n.String()]
return v != nil
}
func (o *baseObject) hasOwnPropertyStr(name string) bool {
v := o.values[name]
return v != nil
}
func (o *baseObject) _defineOwnProperty(name, existingValue Value, descr propertyDescr, throw bool) (val Value, ok bool) {
getterObj, _ := descr.Getter.(*Object)
setterObj, _ := descr.Setter.(*Object)
var existing *valueProperty
if existingValue == nil {
if !o.extensible {
o.val.runtime.typeErrorResult(throw)
return nil, false
}
existing = &valueProperty{}
} else {
if existing, ok = existingValue.(*valueProperty); !ok {
existing = &valueProperty{
writable: true,
enumerable: true,
configurable: true,
value: existingValue,
}
}
if !existing.configurable {
if descr.Configurable == FLAG_TRUE {
goto Reject
}
if descr.Enumerable != FLAG_NOT_SET && descr.Enumerable.Bool() != existing.enumerable {
goto Reject
}
}
if existing.accessor && descr.Value != nil || !existing.accessor && (getterObj != nil || setterObj != nil) {
if !existing.configurable {
goto Reject
}
} else if !existing.accessor {
if !existing.configurable {
if !existing.writable {
if descr.Writable == FLAG_TRUE {
goto Reject
}
if descr.Value != nil && !descr.Value.SameAs(existing.value) {
goto Reject
}
}
}
} else {
if !existing.configurable {
if descr.Getter != nil && existing.getterFunc != getterObj || descr.Setter != nil && existing.setterFunc != setterObj {
goto Reject
}
}
}
}
if descr.Writable == FLAG_TRUE && descr.Enumerable == FLAG_TRUE && descr.Configurable == FLAG_TRUE && descr.Value != nil {
return descr.Value, true
}
if descr.Writable != FLAG_NOT_SET {
existing.writable = descr.Writable.Bool()
}
if descr.Enumerable != FLAG_NOT_SET {
existing.enumerable = descr.Enumerable.Bool()
}
if descr.Configurable != FLAG_NOT_SET {
existing.configurable = descr.Configurable.Bool()
}
if descr.Value != nil {
existing.value = descr.Value
existing.getterFunc = nil
existing.setterFunc = nil
}
if descr.Value != nil || descr.Writable != FLAG_NOT_SET {
existing.accessor = false
}
if descr.Getter != nil {
existing.getterFunc = propGetter(o.val, descr.Getter, o.val.runtime)
existing.value = nil
existing.accessor = true
}
if descr.Setter != nil {
existing.setterFunc = propSetter(o.val, descr.Setter, o.val.runtime)
existing.value = nil
existing.accessor = true
}
if !existing.accessor && existing.value == nil {
existing.value = _undefined
}
return existing, true
Reject:
o.val.runtime.typeErrorResult(throw, "Cannot redefine property: %s", name.ToString())
return nil, false
}
func (o *baseObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
name := n.String()
existingVal := o.values[name]
if v, ok := o._defineOwnProperty(n, existingVal, descr, throw); ok {
o.values[name] = v
if existingVal == nil {
o.propNames = append(o.propNames, name)
}
return true
}
return false
}
func (o *baseObject) _put(name string, v Value) {
if _, exists := o.values[name]; !exists {
o.propNames = append(o.propNames, name)
}
o.values[name] = v
}
func (o *baseObject) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
if writable && enumerable && configurable {
o._put(name, value)
return value
} else {
p := &valueProperty{
value: value,
writable: writable,
enumerable: enumerable,
configurable: configurable,
}
o._put(name, p)
return p
}
}
func (o *baseObject) tryPrimitive(methodName string) Value {
if method, ok := o.getStr(methodName).(*Object); ok {
if call, ok := method.self.assertCallable(); ok {
v := call(FunctionCall{
This: o.val,
})
if _, fail := v.(*Object); !fail {
return v
}
}
}
return nil
}
func (o *baseObject) toPrimitiveNumber() Value {
if v := o.tryPrimitive("valueOf"); v != nil {
return v
}
if v := o.tryPrimitive("toString"); v != nil {
return v
}
o.val.runtime.typeErrorResult(true, "Could not convert %v to primitive", o)
return nil
}
func (o *baseObject) toPrimitiveString() Value {
if v := o.tryPrimitive("toString"); v != nil {
return v
}
if v := o.tryPrimitive("valueOf"); v != nil {
return v
}
o.val.runtime.typeErrorResult(true, "Could not convert %v to primitive", o)
return nil
}
func (o *baseObject) toPrimitive() Value {
return o.toPrimitiveNumber()
}
func (o *baseObject) assertCallable() (func(FunctionCall) Value, bool) {
return nil, false
}
func (o *baseObject) proto() *Object {
return o.prototype
}
func (o *baseObject) isExtensible() bool {
return o.extensible
}
func (o *baseObject) preventExtensions() {
o.extensible = false
}
func (o *baseObject) sortLen() int64 {
return toLength(o.val.self.getStr("length"))
}
func (o *baseObject) sortGet(i int64) Value {
return o.val.self.get(intToValue(i))
}
func (o *baseObject) swap(i, j int64) {
ii := intToValue(i)
jj := intToValue(j)
x := o.val.self.get(ii)
y := o.val.self.get(jj)
o.val.self.put(ii, y, false)
o.val.self.put(jj, x, false)
}
func (o *baseObject) export() interface{} {
m := make(map[string]interface{})
for item, f := o.enumerate(false, false)(); f != nil; item, f = f() {
v := item.value
if v == nil {
v = o.getStr(item.name)
}
if v != nil {
m[item.name] = v.Export()
} else {
m[item.name] = nil
}
}
return m
}
func (o *baseObject) exportType() reflect.Type {
return reflectTypeMap
}
type enumerableFlag int
const (
_ENUM_UNKNOWN enumerableFlag = iota
_ENUM_FALSE
_ENUM_TRUE
)
type propIterItem struct {
name string
value Value // set only when enumerable == _ENUM_UNKNOWN
enumerable enumerableFlag
}
type objectPropIter struct {
o *baseObject
propNames []string
recursive bool
idx int
}
type propFilterIter struct {
wrapped iterNextFunc
all bool
seen map[string]bool
}
func (i *propFilterIter) next() (propIterItem, iterNextFunc) {
for {
var item propIterItem
item, i.wrapped = i.wrapped()
if i.wrapped == nil {
return propIterItem{}, nil
}
if !i.seen[item.name] {
i.seen[item.name] = true
if !i.all {
if item.enumerable == _ENUM_FALSE {
continue
}
if item.enumerable == _ENUM_UNKNOWN {
if prop, ok := item.value.(*valueProperty); ok {
if !prop.enumerable {
continue
}
}
}
}
return item, i.next
}
}
}
func (i *objectPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.propNames) {
name := i.propNames[i.idx]
i.idx++
prop := i.o.values[name]
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
if i.recursive && i.o.prototype != nil {
return i.o.prototype.self._enumerate(i.recursive)()
}
return propIterItem{}, nil
}
func (o *baseObject) _enumerate(recursive bool) iterNextFunc {
propNames := make([]string, len(o.propNames))
copy(propNames, o.propNames)
return (&objectPropIter{
o: o,
propNames: propNames,
recursive: recursive,
}).next
}
func (o *baseObject) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *baseObject) equal(other objectImpl) bool {
// Rely on parent reference comparison
return false
}
func (o *baseObject) hasInstance(v Value) bool {
o.val.runtime.typeErrorResult(true, "Expecting a function in instanceof check, but got %s", o.val.ToString())
panic("Unreachable")
}

150
vendor/github.com/dop251/goja/object_args.go generated vendored Normal file
View file

@ -0,0 +1,150 @@
package goja
type argumentsObject struct {
baseObject
length int
}
type mappedProperty struct {
valueProperty
v *Value
}
func (a *argumentsObject) getPropStr(name string) Value {
if prop, ok := a.values[name].(*mappedProperty); ok {
return *prop.v
}
return a.baseObject.getPropStr(name)
}
func (a *argumentsObject) getProp(n Value) Value {
return a.getPropStr(n.String())
}
func (a *argumentsObject) init() {
a.baseObject.init()
a._putProp("length", intToValue(int64(a.length)), true, false, true)
}
func (a *argumentsObject) put(n Value, val Value, throw bool) {
a.putStr(n.String(), val, throw)
}
func (a *argumentsObject) putStr(name string, val Value, throw bool) {
if prop, ok := a.values[name].(*mappedProperty); ok {
if !prop.writable {
a.val.runtime.typeErrorResult(throw, "Property is not writable: %s", name)
return
}
*prop.v = val
return
}
a.baseObject.putStr(name, val, throw)
}
func (a *argumentsObject) deleteStr(name string, throw bool) bool {
if prop, ok := a.values[name].(*mappedProperty); ok {
if !a.checkDeleteProp(name, &prop.valueProperty, throw) {
return false
}
a._delete(name)
return true
}
return a.baseObject.deleteStr(name, throw)
}
func (a *argumentsObject) delete(n Value, throw bool) bool {
return a.deleteStr(n.String(), throw)
}
type argumentsPropIter1 struct {
a *argumentsObject
idx int
recursive bool
}
type argumentsPropIter struct {
wrapped iterNextFunc
}
func (i *argumentsPropIter) next() (propIterItem, iterNextFunc) {
var item propIterItem
item, i.wrapped = i.wrapped()
if i.wrapped == nil {
return propIterItem{}, nil
}
if prop, ok := item.value.(*mappedProperty); ok {
item.value = *prop.v
}
return item, i.next
}
func (a *argumentsObject) _enumerate(recursive bool) iterNextFunc {
return (&argumentsPropIter{
wrapped: a.baseObject._enumerate(recursive),
}).next
}
func (a *argumentsObject) enumerate(all, recursive bool) iterNextFunc {
return (&argumentsPropIter{
wrapped: a.baseObject.enumerate(all, recursive),
}).next
}
func (a *argumentsObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
name := n.String()
if mapped, ok := a.values[name].(*mappedProperty); ok {
existing := &valueProperty{
configurable: mapped.configurable,
writable: true,
enumerable: mapped.enumerable,
value: mapped.get(a.val),
}
val, ok := a.baseObject._defineOwnProperty(n, existing, descr, throw)
if !ok {
return false
}
if prop, ok := val.(*valueProperty); ok {
if !prop.accessor {
*mapped.v = prop.value
}
if prop.accessor || !prop.writable {
a._put(name, prop)
return true
}
mapped.configurable = prop.configurable
mapped.enumerable = prop.enumerable
} else {
*mapped.v = val
mapped.configurable = true
mapped.enumerable = true
}
return true
}
return a.baseObject.defineOwnProperty(n, descr, throw)
}
func (a *argumentsObject) getOwnProp(name string) Value {
if mapped, ok := a.values[name].(*mappedProperty); ok {
return *mapped.v
}
return a.baseObject.getOwnProp(name)
}
func (a *argumentsObject) export() interface{} {
arr := make([]interface{}, a.length)
for i, _ := range arr {
v := a.get(intToValue(int64(i)))
if v != nil {
arr[i] = v.Export()
}
}
return arr
}

221
vendor/github.com/dop251/goja/object_gomap.go generated vendored Normal file
View file

@ -0,0 +1,221 @@
package goja
import (
"reflect"
"strconv"
)
type objectGoMapSimple struct {
baseObject
data map[string]interface{}
}
func (o *objectGoMapSimple) init() {
o.baseObject.init()
o.prototype = o.val.runtime.global.ObjectPrototype
o.class = classObject
o.extensible = true
}
func (o *objectGoMapSimple) _get(n Value) Value {
return o._getStr(n.String())
}
func (o *objectGoMapSimple) _getStr(name string) Value {
v, exists := o.data[name]
if !exists {
return nil
}
return o.val.runtime.ToValue(v)
}
func (o *objectGoMapSimple) get(n Value) Value {
return o.getStr(n.String())
}
func (o *objectGoMapSimple) getProp(n Value) Value {
return o.getPropStr(n.String())
}
func (o *objectGoMapSimple) getPropStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.baseObject.getPropStr(name)
}
func (o *objectGoMapSimple) getStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.baseObject._getStr(name)
}
func (o *objectGoMapSimple) getOwnProp(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.baseObject.getOwnProp(name)
}
func (o *objectGoMapSimple) put(n Value, val Value, throw bool) {
o.putStr(n.String(), val, throw)
}
func (o *objectGoMapSimple) _hasStr(name string) bool {
_, exists := o.data[name]
return exists
}
func (o *objectGoMapSimple) _has(n Value) bool {
return o._hasStr(n.String())
}
func (o *objectGoMapSimple) putStr(name string, val Value, throw bool) {
if o.extensible || o._hasStr(name) {
o.data[name] = val.Export()
} else {
o.val.runtime.typeErrorResult(throw, "Host object is not extensible")
}
}
func (o *objectGoMapSimple) hasProperty(n Value) bool {
if o._has(n) {
return true
}
return o.baseObject.hasProperty(n)
}
func (o *objectGoMapSimple) hasPropertyStr(name string) bool {
if o._hasStr(name) {
return true
}
return o.baseObject.hasOwnPropertyStr(name)
}
func (o *objectGoMapSimple) hasOwnProperty(n Value) bool {
return o._has(n)
}
func (o *objectGoMapSimple) hasOwnPropertyStr(name string) bool {
return o._hasStr(name)
}
func (o *objectGoMapSimple) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
o.putStr(name, value, false)
return value
}
func (o *objectGoMapSimple) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
if descr.Getter != nil || descr.Setter != nil {
o.val.runtime.typeErrorResult(throw, "Host objects do not support accessor properties")
return false
}
o.put(name, descr.Value, throw)
return true
}
/*
func (o *objectGoMapSimple) toPrimitiveNumber() Value {
return o.toPrimitiveString()
}
func (o *objectGoMapSimple) toPrimitiveString() Value {
return stringObjectObject
}
func (o *objectGoMapSimple) toPrimitive() Value {
return o.toPrimitiveString()
}
func (o *objectGoMapSimple) assertCallable() (call func(FunctionCall) Value, ok bool) {
return nil, false
}
*/
func (o *objectGoMapSimple) deleteStr(name string, throw bool) bool {
delete(o.data, name)
return true
}
func (o *objectGoMapSimple) delete(name Value, throw bool) bool {
return o.deleteStr(name.String(), throw)
}
type gomapPropIter struct {
o *objectGoMapSimple
propNames []string
recursive bool
idx int
}
func (i *gomapPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.propNames) {
name := i.propNames[i.idx]
i.idx++
if _, exists := i.o.data[name]; exists {
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.next
}
}
if i.recursive {
return i.o.prototype.self._enumerate(true)()
}
return propIterItem{}, nil
}
func (o *objectGoMapSimple) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *objectGoMapSimple) _enumerate(recursive bool) iterNextFunc {
propNames := make([]string, len(o.data))
i := 0
for key, _ := range o.data {
propNames[i] = key
i++
}
return (&gomapPropIter{
o: o,
propNames: propNames,
recursive: recursive,
}).next
}
func (o *objectGoMapSimple) export() interface{} {
return o.data
}
func (o *objectGoMapSimple) exportType() reflect.Type {
return reflectTypeMap
}
func (o *objectGoMapSimple) equal(other objectImpl) bool {
if other, ok := other.(*objectGoMapSimple); ok {
return o == other
}
return false
}
func (o *objectGoMapSimple) sortLen() int64 {
return int64(len(o.data))
}
func (o *objectGoMapSimple) sortGet(i int64) Value {
return o.getStr(strconv.FormatInt(i, 10))
}
func (o *objectGoMapSimple) swap(i, j int64) {
ii := strconv.FormatInt(i, 10)
jj := strconv.FormatInt(j, 10)
x := o.getStr(ii)
y := o.getStr(jj)
o.putStr(ii, y, false)
o.putStr(jj, x, false)
}

203
vendor/github.com/dop251/goja/object_gomap_reflect.go generated vendored Normal file
View file

@ -0,0 +1,203 @@
package goja
import "reflect"
type objectGoMapReflect struct {
objectGoReflect
keyType, valueType reflect.Type
}
func (o *objectGoMapReflect) init() {
o.objectGoReflect.init()
o.keyType = o.value.Type().Key()
o.valueType = o.value.Type().Elem()
}
func (o *objectGoMapReflect) toKey(n Value) reflect.Value {
key, err := o.val.runtime.toReflectValue(n, o.keyType)
if err != nil {
o.val.runtime.typeErrorResult(true, "map key conversion error: %v", err)
panic("unreachable")
}
return key
}
func (o *objectGoMapReflect) strToKey(name string) reflect.Value {
if o.keyType.Kind() == reflect.String {
return reflect.ValueOf(name).Convert(o.keyType)
}
return o.toKey(newStringValue(name))
}
func (o *objectGoMapReflect) _get(n Value) Value {
if v := o.value.MapIndex(o.toKey(n)); v.IsValid() {
return o.val.runtime.ToValue(v.Interface())
}
return nil
}
func (o *objectGoMapReflect) _getStr(name string) Value {
if v := o.value.MapIndex(o.strToKey(name)); v.IsValid() {
return o.val.runtime.ToValue(v.Interface())
}
return nil
}
func (o *objectGoMapReflect) get(n Value) Value {
if v := o._get(n); v != nil {
return v
}
return o.objectGoReflect.get(n)
}
func (o *objectGoMapReflect) getStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.objectGoReflect.getStr(name)
}
func (o *objectGoMapReflect) getProp(n Value) Value {
return o.get(n)
}
func (o *objectGoMapReflect) getPropStr(name string) Value {
return o.getStr(name)
}
func (o *objectGoMapReflect) getOwnProp(name string) Value {
if v := o._getStr(name); v != nil {
return &valueProperty{
value: v,
writable: true,
enumerable: true,
}
}
return o.objectGoReflect.getOwnProp(name)
}
func (o *objectGoMapReflect) toValue(val Value, throw bool) (reflect.Value, bool) {
v, err := o.val.runtime.toReflectValue(val, o.valueType)
if err != nil {
o.val.runtime.typeErrorResult(throw, "map value conversion error: %v", err)
return reflect.Value{}, false
}
return v, true
}
func (o *objectGoMapReflect) put(key, val Value, throw bool) {
k := o.toKey(key)
v, ok := o.toValue(val, throw)
if !ok {
return
}
o.value.SetMapIndex(k, v)
}
func (o *objectGoMapReflect) putStr(name string, val Value, throw bool) {
k := o.strToKey(name)
v, ok := o.toValue(val, throw)
if !ok {
return
}
o.value.SetMapIndex(k, v)
}
func (o *objectGoMapReflect) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
o.putStr(name, value, true)
return value
}
func (o *objectGoMapReflect) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
name := n.String()
if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
return false
}
o.put(n, descr.Value, throw)
return true
}
func (o *objectGoMapReflect) hasOwnPropertyStr(name string) bool {
return o.value.MapIndex(o.strToKey(name)).IsValid()
}
func (o *objectGoMapReflect) hasOwnProperty(n Value) bool {
return o.value.MapIndex(o.toKey(n)).IsValid()
}
func (o *objectGoMapReflect) hasProperty(n Value) bool {
if o.hasOwnProperty(n) {
return true
}
return o.objectGoReflect.hasProperty(n)
}
func (o *objectGoMapReflect) hasPropertyStr(name string) bool {
if o.hasOwnPropertyStr(name) {
return true
}
return o.objectGoReflect.hasPropertyStr(name)
}
func (o *objectGoMapReflect) delete(n Value, throw bool) bool {
o.value.SetMapIndex(o.toKey(n), reflect.Value{})
return true
}
func (o *objectGoMapReflect) deleteStr(name string, throw bool) bool {
o.value.SetMapIndex(o.strToKey(name), reflect.Value{})
return true
}
type gomapReflectPropIter struct {
o *objectGoMapReflect
keys []reflect.Value
idx int
recursive bool
}
func (i *gomapReflectPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.keys) {
key := i.keys[i.idx]
v := i.o.value.MapIndex(key)
i.idx++
if v.IsValid() {
return propIterItem{name: key.String(), enumerable: _ENUM_TRUE}, i.next
}
}
if i.recursive {
return i.o.objectGoReflect._enumerate(true)()
}
return propIterItem{}, nil
}
func (o *objectGoMapReflect) _enumerate(recusrive bool) iterNextFunc {
r := &gomapReflectPropIter{
o: o,
keys: o.value.MapKeys(),
recursive: recusrive,
}
return r.next
}
func (o *objectGoMapReflect) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *objectGoMapReflect) equal(other objectImpl) bool {
if other, ok := other.(*objectGoMapReflect); ok {
return o.value.Interface() == other.value.Interface()
}
return false
}

518
vendor/github.com/dop251/goja/object_goreflect.go generated vendored Normal file
View file

@ -0,0 +1,518 @@
package goja
import (
"fmt"
"go/ast"
"reflect"
)
// JsonEncodable allows custom JSON encoding by JSON.stringify()
// Note that if the returned value itself also implements JsonEncodable, it won't have any effect.
type JsonEncodable interface {
JsonEncodable() interface{}
}
// FieldNameMapper provides custom mapping between Go and JavaScript property names.
type FieldNameMapper interface {
// FieldName returns a JavaScript name for the given struct field in the given type.
// If this method returns "" the field becomes hidden.
FieldName(t reflect.Type, f reflect.StructField) string
// FieldName returns a JavaScript name for the given method in the given type.
// If this method returns "" the method becomes hidden.
MethodName(t reflect.Type, m reflect.Method) string
}
type reflectFieldInfo struct {
Index []int
Anonymous bool
}
type reflectTypeInfo struct {
Fields map[string]reflectFieldInfo
Methods map[string]int
FieldNames, MethodNames []string
}
type objectGoReflect struct {
baseObject
origValue, value reflect.Value
valueTypeInfo, origValueTypeInfo *reflectTypeInfo
toJson func() interface{}
}
func (o *objectGoReflect) init() {
o.baseObject.init()
switch o.value.Kind() {
case reflect.Bool:
o.class = classBoolean
o.prototype = o.val.runtime.global.BooleanPrototype
case reflect.String:
o.class = classString
o.prototype = o.val.runtime.global.StringPrototype
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
o.class = classNumber
o.prototype = o.val.runtime.global.NumberPrototype
default:
o.class = classObject
o.prototype = o.val.runtime.global.ObjectPrototype
}
o.baseObject._putProp("toString", o.val.runtime.newNativeFunc(o.toStringFunc, nil, "toString", nil, 0), true, false, true)
o.baseObject._putProp("valueOf", o.val.runtime.newNativeFunc(o.valueOfFunc, nil, "valueOf", nil, 0), true, false, true)
o.valueTypeInfo = o.val.runtime.typeInfo(o.value.Type())
o.origValueTypeInfo = o.val.runtime.typeInfo(o.origValue.Type())
if j, ok := o.origValue.Interface().(JsonEncodable); ok {
o.toJson = j.JsonEncodable
}
}
func (o *objectGoReflect) toStringFunc(call FunctionCall) Value {
return o.toPrimitiveString()
}
func (o *objectGoReflect) valueOfFunc(call FunctionCall) Value {
return o.toPrimitive()
}
func (o *objectGoReflect) get(n Value) Value {
return o.getStr(n.String())
}
func (o *objectGoReflect) _getField(jsName string) reflect.Value {
if info, exists := o.valueTypeInfo.Fields[jsName]; exists {
v := o.value.FieldByIndex(info.Index)
if info.Anonymous {
v = v.Addr()
}
return v
}
return reflect.Value{}
}
func (o *objectGoReflect) _getMethod(jsName string) reflect.Value {
if idx, exists := o.origValueTypeInfo.Methods[jsName]; exists {
return o.origValue.Method(idx)
}
return reflect.Value{}
}
func (o *objectGoReflect) _get(name string) Value {
if o.value.Kind() == reflect.Struct {
if v := o._getField(name); v.IsValid() {
return o.val.runtime.ToValue(v.Interface())
}
}
if v := o._getMethod(name); v.IsValid() {
return o.val.runtime.ToValue(v.Interface())
}
return nil
}
func (o *objectGoReflect) getStr(name string) Value {
if v := o._get(name); v != nil {
return v
}
return o.baseObject._getStr(name)
}
func (o *objectGoReflect) getProp(n Value) Value {
name := n.String()
if p := o.getOwnProp(name); p != nil {
return p
}
return o.baseObject.getOwnProp(name)
}
func (o *objectGoReflect) getPropStr(name string) Value {
if v := o.getOwnProp(name); v != nil {
return v
}
return o.baseObject.getPropStr(name)
}
func (o *objectGoReflect) getOwnProp(name string) Value {
if o.value.Kind() == reflect.Struct {
if v := o._getField(name); v.IsValid() {
return &valueProperty{
value: o.val.runtime.ToValue(v.Interface()),
writable: v.CanSet(),
enumerable: true,
}
}
}
if v := o._getMethod(name); v.IsValid() {
return &valueProperty{
value: o.val.runtime.ToValue(v.Interface()),
enumerable: true,
}
}
return nil
}
func (o *objectGoReflect) put(n Value, val Value, throw bool) {
o.putStr(n.String(), val, throw)
}
func (o *objectGoReflect) putStr(name string, val Value, throw bool) {
if !o._put(name, val, throw) {
o.val.runtime.typeErrorResult(throw, "Cannot assign to property %s of a host object", name)
}
}
func (o *objectGoReflect) _put(name string, val Value, throw bool) bool {
if o.value.Kind() == reflect.Struct {
if v := o._getField(name); v.IsValid() {
if !v.CanSet() {
o.val.runtime.typeErrorResult(throw, "Cannot assign to a non-addressable or read-only property %s of a host object", name)
return false
}
vv, err := o.val.runtime.toReflectValue(val, v.Type())
if err != nil {
o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err)
return false
}
v.Set(vv)
return true
}
}
return false
}
func (o *objectGoReflect) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
if o._put(name, value, false) {
return value
}
return o.baseObject._putProp(name, value, writable, enumerable, configurable)
}
func (r *Runtime) checkHostObjectPropertyDescr(name string, descr propertyDescr, throw bool) bool {
if descr.Getter != nil || descr.Setter != nil {
r.typeErrorResult(throw, "Host objects do not support accessor properties")
return false
}
if descr.Writable == FLAG_FALSE {
r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name)
return false
}
if descr.Configurable == FLAG_TRUE {
r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name)
return false
}
return true
}
func (o *objectGoReflect) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
name := n.String()
if ast.IsExported(name) {
if o.value.Kind() == reflect.Struct {
if v := o._getField(name); v.IsValid() {
if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
return false
}
val := descr.Value
if val == nil {
val = _undefined
}
vv, err := o.val.runtime.toReflectValue(val, v.Type())
if err != nil {
o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err)
return false
}
v.Set(vv)
return true
}
}
}
return o.baseObject.defineOwnProperty(n, descr, throw)
}
func (o *objectGoReflect) _has(name string) bool {
if !ast.IsExported(name) {
return false
}
if o.value.Kind() == reflect.Struct {
if v := o._getField(name); v.IsValid() {
return true
}
}
if v := o._getMethod(name); v.IsValid() {
return true
}
return false
}
func (o *objectGoReflect) hasProperty(n Value) bool {
name := n.String()
if o._has(name) {
return true
}
return o.baseObject.hasProperty(n)
}
func (o *objectGoReflect) hasPropertyStr(name string) bool {
if o._has(name) {
return true
}
return o.baseObject.hasPropertyStr(name)
}
func (o *objectGoReflect) hasOwnProperty(n Value) bool {
return o._has(n.String())
}
func (o *objectGoReflect) hasOwnPropertyStr(name string) bool {
return o._has(name)
}
func (o *objectGoReflect) _toNumber() Value {
switch o.value.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intToValue(o.value.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return intToValue(int64(o.value.Uint()))
case reflect.Bool:
if o.value.Bool() {
return intToValue(1)
} else {
return intToValue(0)
}
case reflect.Float32, reflect.Float64:
return floatToValue(o.value.Float())
}
return nil
}
func (o *objectGoReflect) _toString() Value {
switch o.value.Kind() {
case reflect.String:
return newStringValue(o.value.String())
case reflect.Bool:
if o.value.Interface().(bool) {
return stringTrue
} else {
return stringFalse
}
}
switch v := o.value.Interface().(type) {
case fmt.Stringer:
return newStringValue(v.String())
}
return stringObjectObject
}
func (o *objectGoReflect) toPrimitiveNumber() Value {
if v := o._toNumber(); v != nil {
return v
}
return o._toString()
}
func (o *objectGoReflect) toPrimitiveString() Value {
if v := o._toNumber(); v != nil {
return v.ToString()
}
return o._toString()
}
func (o *objectGoReflect) toPrimitive() Value {
if o.prototype == o.val.runtime.global.NumberPrototype {
return o.toPrimitiveNumber()
}
return o.toPrimitiveString()
}
func (o *objectGoReflect) deleteStr(name string, throw bool) bool {
if o._has(name) {
o.val.runtime.typeErrorResult(throw, "Cannot delete property %s from a Go type")
return false
}
return o.baseObject.deleteStr(name, throw)
}
func (o *objectGoReflect) delete(name Value, throw bool) bool {
return o.deleteStr(name.String(), throw)
}
type goreflectPropIter struct {
o *objectGoReflect
idx int
recursive bool
}
func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) {
names := i.o.valueTypeInfo.FieldNames
if i.idx < len(names) {
name := names[i.idx]
i.idx++
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.nextField
}
i.idx = 0
return i.nextMethod()
}
func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) {
names := i.o.origValueTypeInfo.MethodNames
if i.idx < len(names) {
name := names[i.idx]
i.idx++
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.nextMethod
}
if i.recursive {
return i.o.baseObject._enumerate(true)()
}
return propIterItem{}, nil
}
func (o *objectGoReflect) _enumerate(recursive bool) iterNextFunc {
r := &goreflectPropIter{
o: o,
recursive: recursive,
}
if o.value.Kind() == reflect.Struct {
return r.nextField
}
return r.nextMethod
}
func (o *objectGoReflect) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *objectGoReflect) export() interface{} {
return o.origValue.Interface()
}
func (o *objectGoReflect) exportType() reflect.Type {
return o.origValue.Type()
}
func (o *objectGoReflect) equal(other objectImpl) bool {
if other, ok := other.(*objectGoReflect); ok {
return o.value.Interface() == other.value.Interface()
}
return false
}
func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectTypeInfo) {
n := t.NumField()
for i := 0; i < n; i++ {
field := t.Field(i)
name := field.Name
if !ast.IsExported(name) {
continue
}
if r.fieldNameMapper != nil {
name = r.fieldNameMapper.FieldName(t, field)
}
if name != "" {
if inf, exists := info.Fields[name]; !exists {
info.FieldNames = append(info.FieldNames, name)
} else {
if len(inf.Index) <= len(index) {
continue
}
}
}
if name != "" || field.Anonymous {
idx := make([]int, len(index)+1)
copy(idx, index)
idx[len(idx)-1] = i
if name != "" {
info.Fields[name] = reflectFieldInfo{
Index: idx,
Anonymous: field.Anonymous,
}
}
if field.Anonymous {
typ := field.Type
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() == reflect.Struct {
r.buildFieldInfo(typ, idx, info)
}
}
}
}
}
func (r *Runtime) buildTypeInfo(t reflect.Type) (info *reflectTypeInfo) {
info = new(reflectTypeInfo)
if t.Kind() == reflect.Struct {
info.Fields = make(map[string]reflectFieldInfo)
n := t.NumField()
info.FieldNames = make([]string, 0, n)
r.buildFieldInfo(t, nil, info)
}
info.Methods = make(map[string]int)
n := t.NumMethod()
info.MethodNames = make([]string, 0, n)
for i := 0; i < n; i++ {
method := t.Method(i)
name := method.Name
if !ast.IsExported(name) {
continue
}
if r.fieldNameMapper != nil {
name = r.fieldNameMapper.MethodName(t, method)
if name == "" {
continue
}
}
if _, exists := info.Methods[name]; !exists {
info.MethodNames = append(info.MethodNames, name)
}
info.Methods[name] = i
}
return
}
func (r *Runtime) typeInfo(t reflect.Type) (info *reflectTypeInfo) {
var exists bool
if info, exists = r.typeInfoCache[t]; !exists {
info = r.buildTypeInfo(t)
if r.typeInfoCache == nil {
r.typeInfoCache = make(map[reflect.Type]*reflectTypeInfo)
}
r.typeInfoCache[t] = info
}
return
}
// Sets a custom field name mapper for Go types. It can be called at any time, however
// the mapping for any given value is fixed at the point of creation.
// Setting this to nil restores the default behaviour which is all exported fields and methods are mapped to their
// original unchanged names.
func (r *Runtime) SetFieldNameMapper(mapper FieldNameMapper) {
r.fieldNameMapper = mapper
r.typeInfoCache = nil
}

303
vendor/github.com/dop251/goja/object_goslice.go generated vendored Normal file
View file

@ -0,0 +1,303 @@
package goja
import (
"reflect"
"strconv"
)
type objectGoSlice struct {
baseObject
data *[]interface{}
lengthProp valueProperty
sliceExtensible bool
}
func (o *objectGoSlice) init() {
o.baseObject.init()
o.class = classArray
o.prototype = o.val.runtime.global.ArrayPrototype
o.lengthProp.writable = o.sliceExtensible
o._setLen()
o.baseObject._put("length", &o.lengthProp)
}
func (o *objectGoSlice) _setLen() {
o.lengthProp.value = intToValue(int64(len(*o.data)))
}
func (o *objectGoSlice) getIdx(idx int64) Value {
if idx < int64(len(*o.data)) {
return o.val.runtime.ToValue((*o.data)[idx])
}
return nil
}
func (o *objectGoSlice) _get(n Value) Value {
if idx := toIdx(n); idx >= 0 {
return o.getIdx(idx)
}
return nil
}
func (o *objectGoSlice) _getStr(name string) Value {
if idx := strToIdx(name); idx >= 0 {
return o.getIdx(idx)
}
return nil
}
func (o *objectGoSlice) get(n Value) Value {
if v := o._get(n); v != nil {
return v
}
return o.baseObject._getStr(n.String())
}
func (o *objectGoSlice) getStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.baseObject._getStr(name)
}
func (o *objectGoSlice) getProp(n Value) Value {
if v := o._get(n); v != nil {
return v
}
return o.baseObject.getPropStr(n.String())
}
func (o *objectGoSlice) getPropStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.baseObject.getPropStr(name)
}
func (o *objectGoSlice) getOwnProp(name string) Value {
if v := o._getStr(name); v != nil {
return &valueProperty{
value: v,
writable: true,
enumerable: true,
}
}
return o.baseObject.getOwnProp(name)
}
func (o *objectGoSlice) grow(size int64) {
newcap := int64(cap(*o.data))
if newcap < size {
// Use the same algorithm as in runtime.growSlice
doublecap := newcap + newcap
if size > doublecap {
newcap = size
} else {
if len(*o.data) < 1024 {
newcap = doublecap
} else {
for newcap < size {
newcap += newcap / 4
}
}
}
n := make([]interface{}, size, newcap)
copy(n, *o.data)
*o.data = n
} else {
*o.data = (*o.data)[:size]
}
o._setLen()
}
func (o *objectGoSlice) putIdx(idx int64, v Value, throw bool) {
if idx >= int64(len(*o.data)) {
if !o.sliceExtensible {
o.val.runtime.typeErrorResult(throw, "Cannot extend Go slice")
return
}
o.grow(idx + 1)
}
(*o.data)[idx] = v.Export()
}
func (o *objectGoSlice) put(n Value, val Value, throw bool) {
if idx := toIdx(n); idx >= 0 {
o.putIdx(idx, val, throw)
return
}
// TODO: length
o.baseObject.put(n, val, throw)
}
func (o *objectGoSlice) putStr(name string, val Value, throw bool) {
if idx := strToIdx(name); idx >= 0 {
o.putIdx(idx, val, throw)
return
}
// TODO: length
o.baseObject.putStr(name, val, throw)
}
func (o *objectGoSlice) _has(n Value) bool {
if idx := toIdx(n); idx >= 0 {
return idx < int64(len(*o.data))
}
return false
}
func (o *objectGoSlice) _hasStr(name string) bool {
if idx := strToIdx(name); idx >= 0 {
return idx < int64(len(*o.data))
}
return false
}
func (o *objectGoSlice) hasProperty(n Value) bool {
if o._has(n) {
return true
}
return o.baseObject.hasProperty(n)
}
func (o *objectGoSlice) hasPropertyStr(name string) bool {
if o._hasStr(name) {
return true
}
return o.baseObject.hasPropertyStr(name)
}
func (o *objectGoSlice) hasOwnProperty(n Value) bool {
if o._has(n) {
return true
}
return o.baseObject.hasOwnProperty(n)
}
func (o *objectGoSlice) hasOwnPropertyStr(name string) bool {
if o._hasStr(name) {
return true
}
return o.baseObject.hasOwnPropertyStr(name)
}
func (o *objectGoSlice) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
o.putStr(name, value, false)
return value
}
func (o *objectGoSlice) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
if idx := toIdx(n); idx >= 0 {
if !o.val.runtime.checkHostObjectPropertyDescr(n.String(), descr, throw) {
return false
}
val := descr.Value
if val == nil {
val = _undefined
}
o.putIdx(idx, val, throw)
return true
}
return o.baseObject.defineOwnProperty(n, descr, throw)
}
func (o *objectGoSlice) toPrimitiveNumber() Value {
return o.toPrimitiveString()
}
func (o *objectGoSlice) toPrimitiveString() Value {
return o.val.runtime.arrayproto_join(FunctionCall{
This: o.val,
})
}
func (o *objectGoSlice) toPrimitive() Value {
return o.toPrimitiveString()
}
func (o *objectGoSlice) deleteStr(name string, throw bool) bool {
if idx := strToIdx(name); idx >= 0 && idx < int64(len(*o.data)) {
(*o.data)[idx] = nil
return true
}
return o.baseObject.deleteStr(name, throw)
}
func (o *objectGoSlice) delete(name Value, throw bool) bool {
if idx := toIdx(name); idx >= 0 && idx < int64(len(*o.data)) {
(*o.data)[idx] = nil
return true
}
return o.baseObject.delete(name, throw)
}
type goslicePropIter struct {
o *objectGoSlice
recursive bool
idx, limit int
}
func (i *goslicePropIter) next() (propIterItem, iterNextFunc) {
if i.idx < i.limit && i.idx < len(*i.o.data) {
name := strconv.Itoa(i.idx)
i.idx++
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.next
}
if i.recursive {
return i.o.prototype.self._enumerate(i.recursive)()
}
return propIterItem{}, nil
}
func (o *objectGoSlice) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *objectGoSlice) _enumerate(recursive bool) iterNextFunc {
return (&goslicePropIter{
o: o,
recursive: recursive,
limit: len(*o.data),
}).next
}
func (o *objectGoSlice) export() interface{} {
return *o.data
}
func (o *objectGoSlice) exportType() reflect.Type {
return reflectTypeArray
}
func (o *objectGoSlice) equal(other objectImpl) bool {
if other, ok := other.(*objectGoSlice); ok {
return o.data == other.data
}
return false
}
func (o *objectGoSlice) sortLen() int64 {
return int64(len(*o.data))
}
func (o *objectGoSlice) sortGet(i int64) Value {
return o.get(intToValue(i))
}
func (o *objectGoSlice) swap(i, j int64) {
ii := intToValue(i)
jj := intToValue(j)
x := o.get(ii)
y := o.get(jj)
o.put(ii, y, false)
o.put(jj, x, false)
}

250
vendor/github.com/dop251/goja/object_goslice_reflect.go generated vendored Normal file
View file

@ -0,0 +1,250 @@
package goja
import (
"reflect"
"strconv"
)
type objectGoSliceReflect struct {
objectGoReflect
lengthProp valueProperty
}
func (o *objectGoSliceReflect) init() {
o.objectGoReflect.init()
o.class = classArray
o.prototype = o.val.runtime.global.ArrayPrototype
o.lengthProp.writable = false
o._setLen()
o.baseObject._put("length", &o.lengthProp)
}
func (o *objectGoSliceReflect) _setLen() {
o.lengthProp.value = intToValue(int64(o.value.Len()))
}
func (o *objectGoSliceReflect) _has(n Value) bool {
if idx := toIdx(n); idx >= 0 {
return idx < int64(o.value.Len())
}
return false
}
func (o *objectGoSliceReflect) _hasStr(name string) bool {
if idx := strToIdx(name); idx >= 0 {
return idx < int64(o.value.Len())
}
return false
}
func (o *objectGoSliceReflect) getIdx(idx int64) Value {
if idx < int64(o.value.Len()) {
return o.val.runtime.ToValue(o.value.Index(int(idx)).Interface())
}
return nil
}
func (o *objectGoSliceReflect) _get(n Value) Value {
if idx := toIdx(n); idx >= 0 {
return o.getIdx(idx)
}
return nil
}
func (o *objectGoSliceReflect) _getStr(name string) Value {
if idx := strToIdx(name); idx >= 0 {
return o.getIdx(idx)
}
return nil
}
func (o *objectGoSliceReflect) get(n Value) Value {
if v := o._get(n); v != nil {
return v
}
return o.objectGoReflect.get(n)
}
func (o *objectGoSliceReflect) getProp(n Value) Value {
if v := o._get(n); v != nil {
return v
}
return o.objectGoReflect.getProp(n)
}
func (o *objectGoSliceReflect) getPropStr(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.objectGoReflect.getPropStr(name)
}
func (o *objectGoSliceReflect) getOwnProp(name string) Value {
if v := o._getStr(name); v != nil {
return v
}
return o.objectGoReflect.getOwnProp(name)
}
func (o *objectGoSliceReflect) putIdx(idx int64, v Value, throw bool) {
if idx >= int64(o.value.Len()) {
o.val.runtime.typeErrorResult(throw, "Cannot extend a Go reflect slice")
return
}
val, err := o.val.runtime.toReflectValue(v, o.value.Type().Elem())
if err != nil {
o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err)
return
}
o.value.Index(int(idx)).Set(val)
}
func (o *objectGoSliceReflect) put(n Value, val Value, throw bool) {
if idx := toIdx(n); idx >= 0 {
o.putIdx(idx, val, throw)
return
}
// TODO: length
o.objectGoReflect.put(n, val, throw)
}
func (o *objectGoSliceReflect) putStr(name string, val Value, throw bool) {
if idx := strToIdx(name); idx >= 0 {
o.putIdx(idx, val, throw)
return
}
// TODO: length
o.objectGoReflect.putStr(name, val, throw)
}
func (o *objectGoSliceReflect) hasProperty(n Value) bool {
if o._has(n) {
return true
}
return o.objectGoReflect.hasProperty(n)
}
func (o *objectGoSliceReflect) hasPropertyStr(name string) bool {
if o._hasStr(name) {
return true
}
return o.objectGoReflect.hasOwnPropertyStr(name)
}
func (o *objectGoSliceReflect) hasOwnProperty(n Value) bool {
if o._has(n) {
return true
}
return o.objectGoReflect.hasOwnProperty(n)
}
func (o *objectGoSliceReflect) hasOwnPropertyStr(name string) bool {
if o._hasStr(name) {
return true
}
return o.objectGoReflect.hasOwnPropertyStr(name)
}
func (o *objectGoSliceReflect) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
o.putStr(name, value, false)
return value
}
func (o *objectGoSliceReflect) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
if !o.val.runtime.checkHostObjectPropertyDescr(name.String(), descr, throw) {
return false
}
o.put(name, descr.Value, throw)
return true
}
func (o *objectGoSliceReflect) toPrimitiveNumber() Value {
return o.toPrimitiveString()
}
func (o *objectGoSliceReflect) toPrimitiveString() Value {
return o.val.runtime.arrayproto_join(FunctionCall{
This: o.val,
})
}
func (o *objectGoSliceReflect) toPrimitive() Value {
return o.toPrimitiveString()
}
func (o *objectGoSliceReflect) deleteStr(name string, throw bool) bool {
if idx := strToIdx(name); idx >= 0 && idx < int64(o.value.Len()) {
o.value.Index(int(idx)).Set(reflect.Zero(o.value.Type().Elem()))
return true
}
return o.objectGoReflect.deleteStr(name, throw)
}
func (o *objectGoSliceReflect) delete(name Value, throw bool) bool {
if idx := toIdx(name); idx >= 0 && idx < int64(o.value.Len()) {
o.value.Index(int(idx)).Set(reflect.Zero(o.value.Type().Elem()))
return true
}
return true
}
type gosliceReflectPropIter struct {
o *objectGoSliceReflect
recursive bool
idx, limit int
}
func (i *gosliceReflectPropIter) next() (propIterItem, iterNextFunc) {
if i.idx < i.limit && i.idx < i.o.value.Len() {
name := strconv.Itoa(i.idx)
i.idx++
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.next
}
if i.recursive {
return i.o.prototype.self._enumerate(i.recursive)()
}
return propIterItem{}, nil
}
func (o *objectGoSliceReflect) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: o._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (o *objectGoSliceReflect) _enumerate(recursive bool) iterNextFunc {
return (&gosliceReflectPropIter{
o: o,
recursive: recursive,
limit: o.value.Len(),
}).next
}
func (o *objectGoSliceReflect) equal(other objectImpl) bool {
if other, ok := other.(*objectGoSliceReflect); ok {
return o.value.Interface() == other.value.Interface()
}
return false
}
func (o *objectGoSliceReflect) sortLen() int64 {
return int64(o.value.Len())
}
func (o *objectGoSliceReflect) sortGet(i int64) Value {
return o.get(intToValue(i))
}
func (o *objectGoSliceReflect) swap(i, j int64) {
ii := intToValue(i)
jj := intToValue(j)
x := o.get(ii)
y := o.get(jj)
o.put(ii, y, false)
o.put(jj, x, false)
}

200
vendor/github.com/dop251/goja/object_lazy.go generated vendored Normal file
View file

@ -0,0 +1,200 @@
package goja
import "reflect"
type lazyObject struct {
val *Object
create func(*Object) objectImpl
}
func (o *lazyObject) className() string {
obj := o.create(o.val)
o.val.self = obj
return obj.className()
}
func (o *lazyObject) get(n Value) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.get(n)
}
func (o *lazyObject) getProp(n Value) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.getProp(n)
}
func (o *lazyObject) getPropStr(name string) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.getPropStr(name)
}
func (o *lazyObject) getStr(name string) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.getStr(name)
}
func (o *lazyObject) getOwnProp(name string) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.getOwnProp(name)
}
func (o *lazyObject) put(n Value, val Value, throw bool) {
obj := o.create(o.val)
o.val.self = obj
obj.put(n, val, throw)
}
func (o *lazyObject) putStr(name string, val Value, throw bool) {
obj := o.create(o.val)
o.val.self = obj
obj.putStr(name, val, throw)
}
func (o *lazyObject) hasProperty(n Value) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.hasProperty(n)
}
func (o *lazyObject) hasPropertyStr(name string) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.hasPropertyStr(name)
}
func (o *lazyObject) hasOwnProperty(n Value) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.hasOwnProperty(n)
}
func (o *lazyObject) hasOwnPropertyStr(name string) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.hasOwnPropertyStr(name)
}
func (o *lazyObject) _putProp(name string, value Value, writable, enumerable, configurable bool) Value {
obj := o.create(o.val)
o.val.self = obj
return obj._putProp(name, value, writable, enumerable, configurable)
}
func (o *lazyObject) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.defineOwnProperty(name, descr, throw)
}
func (o *lazyObject) toPrimitiveNumber() Value {
obj := o.create(o.val)
o.val.self = obj
return obj.toPrimitiveNumber()
}
func (o *lazyObject) toPrimitiveString() Value {
obj := o.create(o.val)
o.val.self = obj
return obj.toPrimitiveString()
}
func (o *lazyObject) toPrimitive() Value {
obj := o.create(o.val)
o.val.self = obj
return obj.toPrimitive()
}
func (o *lazyObject) assertCallable() (call func(FunctionCall) Value, ok bool) {
obj := o.create(o.val)
o.val.self = obj
return obj.assertCallable()
}
func (o *lazyObject) deleteStr(name string, throw bool) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.deleteStr(name, throw)
}
func (o *lazyObject) delete(name Value, throw bool) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.delete(name, throw)
}
func (o *lazyObject) proto() *Object {
obj := o.create(o.val)
o.val.self = obj
return obj.proto()
}
func (o *lazyObject) hasInstance(v Value) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.hasInstance(v)
}
func (o *lazyObject) isExtensible() bool {
obj := o.create(o.val)
o.val.self = obj
return obj.isExtensible()
}
func (o *lazyObject) preventExtensions() {
obj := o.create(o.val)
o.val.self = obj
obj.preventExtensions()
}
func (o *lazyObject) enumerate(all, recusrive bool) iterNextFunc {
obj := o.create(o.val)
o.val.self = obj
return obj.enumerate(all, recusrive)
}
func (o *lazyObject) _enumerate(recursive bool) iterNextFunc {
obj := o.create(o.val)
o.val.self = obj
return obj._enumerate(recursive)
}
func (o *lazyObject) export() interface{} {
obj := o.create(o.val)
o.val.self = obj
return obj.export()
}
func (o *lazyObject) exportType() reflect.Type {
obj := o.create(o.val)
o.val.self = obj
return obj.exportType()
}
func (o *lazyObject) equal(other objectImpl) bool {
obj := o.create(o.val)
o.val.self = obj
return obj.equal(other)
}
func (o *lazyObject) sortLen() int64 {
obj := o.create(o.val)
o.val.self = obj
return obj.sortLen()
}
func (o *lazyObject) sortGet(i int64) Value {
obj := o.create(o.val)
o.val.self = obj
return obj.sortGet(i)
}
func (o *lazyObject) swap(i, j int64) {
obj := o.create(o.val)
o.val.self = obj
obj.swap(i, j)
}

361
vendor/github.com/dop251/goja/regexp.go generated vendored Normal file
View file

@ -0,0 +1,361 @@
package goja
import (
"fmt"
"github.com/dlclark/regexp2"
"regexp"
"unicode/utf16"
"unicode/utf8"
)
type regexpPattern interface {
FindSubmatchIndex(valueString, int) []int
FindAllSubmatchIndex(valueString, int) [][]int
FindAllSubmatchIndexUTF8(string, int) [][]int
FindAllSubmatchIndexASCII(string, int) [][]int
MatchString(valueString) bool
}
type regexp2Wrapper regexp2.Regexp
type regexpWrapper regexp.Regexp
type regexpObject struct {
baseObject
pattern regexpPattern
source valueString
global, multiline, ignoreCase bool
}
func (r *regexp2Wrapper) FindSubmatchIndex(s valueString, start int) (result []int) {
wrapped := (*regexp2.Regexp)(r)
var match *regexp2.Match
var err error
switch s := s.(type) {
case asciiString:
match, err = wrapped.FindStringMatch(string(s)[start:])
case unicodeString:
match, err = wrapped.FindRunesMatch(utf16.Decode(s[start:]))
default:
panic(fmt.Errorf("Unknown string type: %T", s))
}
if err != nil {
return
}
if match == nil {
return
}
groups := match.Groups()
result = make([]int, 0, len(groups)<<1)
for _, group := range groups {
if len(group.Captures) > 0 {
result = append(result, group.Index, group.Index+group.Length)
} else {
result = append(result, -1, 0)
}
}
return
}
func (r *regexp2Wrapper) FindAllSubmatchIndexUTF8(s string, n int) [][]int {
wrapped := (*regexp2.Regexp)(r)
if n < 0 {
n = len(s) + 1
}
results := make([][]int, 0, n)
idxMap := make([]int, 0, len(s))
runes := make([]rune, 0, len(s))
for pos, rr := range s {
runes = append(runes, rr)
idxMap = append(idxMap, pos)
}
idxMap = append(idxMap, len(s))
match, err := wrapped.FindRunesMatch(runes)
if err != nil {
return nil
}
i := 0
for match != nil && i < n {
groups := match.Groups()
result := make([]int, 0, len(groups)<<1)
for _, group := range groups {
if len(group.Captures) > 0 {
result = append(result, idxMap[group.Index], idxMap[group.Index+group.Length])
} else {
result = append(result, -1, 0)
}
}
results = append(results, result)
match, err = wrapped.FindNextMatch(match)
if err != nil {
return nil
}
i++
}
return results
}
func (r *regexp2Wrapper) FindAllSubmatchIndexASCII(s string, n int) [][]int {
wrapped := (*regexp2.Regexp)(r)
if n < 0 {
n = len(s) + 1
}
results := make([][]int, 0, n)
match, err := wrapped.FindStringMatch(s)
if err != nil {
return nil
}
i := 0
for match != nil && i < n {
groups := match.Groups()
result := make([]int, 0, len(groups)<<1)
for _, group := range groups {
if len(group.Captures) > 0 {
result = append(result, group.Index, group.Index+group.Length)
} else {
result = append(result, -1, 0)
}
}
results = append(results, result)
match, err = wrapped.FindNextMatch(match)
if err != nil {
return nil
}
i++
}
return results
}
func (r *regexp2Wrapper) findAllSubmatchIndexUTF16(s unicodeString, n int) [][]int {
wrapped := (*regexp2.Regexp)(r)
if n < 0 {
n = len(s) + 1
}
results := make([][]int, 0, n)
rd := runeReaderReplace{s.reader(0)}
posMap := make([]int, s.length()+1)
curPos := 0
curRuneIdx := 0
runes := make([]rune, 0, s.length())
for {
rn, size, err := rd.ReadRune()
if err != nil {
break
}
runes = append(runes, rn)
posMap[curRuneIdx] = curPos
curRuneIdx++
curPos += size
}
posMap[curRuneIdx] = curPos
match, err := wrapped.FindRunesMatch(runes)
if err != nil {
return nil
}
for match != nil {
groups := match.Groups()
result := make([]int, 0, len(groups)<<1)
for _, group := range groups {
if len(group.Captures) > 0 {
start := posMap[group.Index]
end := posMap[group.Index+group.Length]
result = append(result, start, end)
} else {
result = append(result, -1, 0)
}
}
results = append(results, result)
match, err = wrapped.FindNextMatch(match)
if err != nil {
return nil
}
}
return results
}
func (r *regexp2Wrapper) FindAllSubmatchIndex(s valueString, n int) [][]int {
switch s := s.(type) {
case asciiString:
return r.FindAllSubmatchIndexASCII(string(s), n)
case unicodeString:
return r.findAllSubmatchIndexUTF16(s, n)
default:
panic("Unsupported string type")
}
}
func (r *regexp2Wrapper) MatchString(s valueString) bool {
wrapped := (*regexp2.Regexp)(r)
switch s := s.(type) {
case asciiString:
matched, _ := wrapped.MatchString(string(s))
return matched
case unicodeString:
matched, _ := wrapped.MatchRunes(utf16.Decode(s))
return matched
default:
panic(fmt.Errorf("Unknown string type: %T", s))
}
}
func (r *regexpWrapper) FindSubmatchIndex(s valueString, start int) (result []int) {
wrapped := (*regexp.Regexp)(r)
return wrapped.FindReaderSubmatchIndex(runeReaderReplace{s.reader(start)})
}
func (r *regexpWrapper) MatchString(s valueString) bool {
wrapped := (*regexp.Regexp)(r)
return wrapped.MatchReader(runeReaderReplace{s.reader(0)})
}
func (r *regexpWrapper) FindAllSubmatchIndex(s valueString, n int) [][]int {
wrapped := (*regexp.Regexp)(r)
switch s := s.(type) {
case asciiString:
return wrapped.FindAllStringSubmatchIndex(string(s), n)
case unicodeString:
return r.findAllSubmatchIndexUTF16(s, n)
default:
panic("Unsupported string type")
}
}
func (r *regexpWrapper) FindAllSubmatchIndexUTF8(s string, n int) [][]int {
wrapped := (*regexp.Regexp)(r)
return wrapped.FindAllStringSubmatchIndex(s, n)
}
func (r *regexpWrapper) FindAllSubmatchIndexASCII(s string, n int) [][]int {
return r.FindAllSubmatchIndexUTF8(s, n)
}
func (r *regexpWrapper) findAllSubmatchIndexUTF16(s unicodeString, n int) [][]int {
wrapped := (*regexp.Regexp)(r)
utf8Bytes := make([]byte, 0, len(s)*2)
posMap := make(map[int]int)
curPos := 0
rd := runeReaderReplace{s.reader(0)}
for {
rn, size, err := rd.ReadRune()
if err != nil {
break
}
l := len(utf8Bytes)
utf8Bytes = append(utf8Bytes, 0, 0, 0, 0)
n := utf8.EncodeRune(utf8Bytes[l:], rn)
utf8Bytes = utf8Bytes[:l+n]
posMap[l] = curPos
curPos += size
}
posMap[len(utf8Bytes)] = curPos
rr := wrapped.FindAllSubmatchIndex(utf8Bytes, n)
for _, res := range rr {
for j, pos := range res {
mapped, exists := posMap[pos]
if !exists {
panic("Unicode match is not on rune boundary")
}
res[j] = mapped
}
}
return rr
}
func (r *regexpObject) execResultToArray(target valueString, result []int) Value {
captureCount := len(result) >> 1
valueArray := make([]Value, captureCount)
matchIndex := result[0]
lowerBound := matchIndex
for index := 0; index < captureCount; index++ {
offset := index << 1
if result[offset] >= lowerBound {
valueArray[index] = target.substring(int64(result[offset]), int64(result[offset+1]))
lowerBound = result[offset]
} else {
valueArray[index] = _undefined
}
}
match := r.val.runtime.newArrayValues(valueArray)
match.self.putStr("input", target, false)
match.self.putStr("index", intToValue(int64(matchIndex)), false)
return match
}
func (r *regexpObject) execRegexp(target valueString) (match bool, result []int) {
lastIndex := int64(0)
if p := r.getStr("lastIndex"); p != nil {
lastIndex = p.ToInteger()
if lastIndex < 0 {
lastIndex = 0
}
}
index := lastIndex
if !r.global {
index = 0
}
if index >= 0 && index <= target.length() {
result = r.pattern.FindSubmatchIndex(target, int(index))
}
if result == nil {
r.putStr("lastIndex", intToValue(0), true)
return
}
match = true
startIndex := index
endIndex := int(lastIndex) + result[1]
// We do this shift here because the .FindStringSubmatchIndex above
// was done on a local subordinate slice of the string, not the whole string
for index, _ := range result {
result[index] += int(startIndex)
}
if r.global {
r.putStr("lastIndex", intToValue(int64(endIndex)), true)
}
return
}
func (r *regexpObject) exec(target valueString) Value {
match, result := r.execRegexp(target)
if match {
return r.execResultToArray(target, result)
}
return _null
}
func (r *regexpObject) test(target valueString) bool {
match, _ := r.execRegexp(target)
return match
}
func (r *regexpObject) clone() *Object {
r1 := r.val.runtime.newRegexpObject(r.prototype)
r1.source = r.source
r1.pattern = r.pattern
r1.global = r.global
r1.ignoreCase = r.ignoreCase
r1.multiline = r.multiline
return r1.val
}
func (r *regexpObject) init() {
r.baseObject.init()
r._putProp("lastIndex", intToValue(0), true, false, false)
}

1536
vendor/github.com/dop251/goja/runtime.go generated vendored Normal file

File diff suppressed because it is too large Load diff

92
vendor/github.com/dop251/goja/srcfile.go generated vendored Normal file
View file

@ -0,0 +1,92 @@
package goja
import (
"fmt"
"github.com/go-sourcemap/sourcemap"
"sort"
"strings"
"sync"
)
type Position struct {
Line, Col int
}
type SrcFile struct {
name string
src string
lineOffsets []int
lineOffsetsLock sync.Mutex
lastScannedOffset int
sourceMap *sourcemap.Consumer
}
func NewSrcFile(name, src string, sourceMap *sourcemap.Consumer) *SrcFile {
return &SrcFile{
name: name,
src: src,
sourceMap: sourceMap,
}
}
func (f *SrcFile) Position(offset int) Position {
var line int
var lineOffsets []int
f.lineOffsetsLock.Lock()
if offset > f.lastScannedOffset {
line = f.scanTo(offset)
lineOffsets = f.lineOffsets
f.lineOffsetsLock.Unlock()
} else {
lineOffsets = f.lineOffsets
f.lineOffsetsLock.Unlock()
line = sort.Search(len(lineOffsets), func(x int) bool { return lineOffsets[x] > offset }) - 1
}
var lineStart int
if line >= 0 {
lineStart = lineOffsets[line]
}
row := line + 2
col := offset - lineStart + 1
if f.sourceMap != nil {
if _, _, row, col, ok := f.sourceMap.Source(row, col); ok {
return Position{
Line: row,
Col: col,
}
}
}
return Position{
Line: row,
Col: col,
}
}
func (f *SrcFile) scanTo(offset int) int {
o := f.lastScannedOffset
for o < offset {
p := strings.Index(f.src[o:], "\n")
if p == -1 {
f.lastScannedOffset = len(f.src)
return len(f.lineOffsets) - 1
}
o = o + p + 1
f.lineOffsets = append(f.lineOffsets, o)
}
f.lastScannedOffset = o
if o == offset {
return len(f.lineOffsets) - 1
}
return len(f.lineOffsets) - 2
}
func (p Position) String() string {
return fmt.Sprintf("%d:%d", p.Line, p.Col)
}

226
vendor/github.com/dop251/goja/string.go generated vendored Normal file
View file

@ -0,0 +1,226 @@
package goja
import (
"io"
"strconv"
"unicode/utf16"
"unicode/utf8"
)
var (
stringTrue valueString = asciiString("true")
stringFalse valueString = asciiString("false")
stringNull valueString = asciiString("null")
stringUndefined valueString = asciiString("undefined")
stringObjectC valueString = asciiString("object")
stringFunction valueString = asciiString("function")
stringBoolean valueString = asciiString("boolean")
stringString valueString = asciiString("string")
stringNumber valueString = asciiString("number")
stringNaN valueString = asciiString("NaN")
stringInfinity = asciiString("Infinity")
stringPlusInfinity = asciiString("+Infinity")
stringNegInfinity = asciiString("-Infinity")
stringEmpty valueString = asciiString("")
string__proto__ valueString = asciiString("__proto__")
stringError valueString = asciiString("Error")
stringTypeError valueString = asciiString("TypeError")
stringReferenceError valueString = asciiString("ReferenceError")
stringSyntaxError valueString = asciiString("SyntaxError")
stringRangeError valueString = asciiString("RangeError")
stringEvalError valueString = asciiString("EvalError")
stringURIError valueString = asciiString("URIError")
stringGoError valueString = asciiString("GoError")
stringObjectNull valueString = asciiString("[object Null]")
stringObjectObject valueString = asciiString("[object Object]")
stringObjectUndefined valueString = asciiString("[object Undefined]")
stringGlobalObject valueString = asciiString("Global Object")
stringInvalidDate valueString = asciiString("Invalid Date")
)
type valueString interface {
Value
charAt(int64) rune
length() int64
concat(valueString) valueString
substring(start, end int64) valueString
compareTo(valueString) int
reader(start int) io.RuneReader
index(valueString, int64) int64
lastIndex(valueString, int64) int64
toLower() valueString
toUpper() valueString
toTrimmedUTF8() string
}
type stringObject struct {
baseObject
value valueString
length int64
lengthProp valueProperty
}
func newUnicodeString(s string) valueString {
return unicodeString(utf16.Encode([]rune(s)))
}
func newStringValue(s string) valueString {
for _, chr := range s {
if chr >= utf8.RuneSelf {
return newUnicodeString(s)
}
}
return asciiString(s)
}
func (s *stringObject) init() {
s.baseObject.init()
s.setLength()
}
func (s *stringObject) setLength() {
if s.value != nil {
s.length = s.value.length()
}
s.lengthProp.value = intToValue(s.length)
s._put("length", &s.lengthProp)
}
func (s *stringObject) get(n Value) Value {
if idx := toIdx(n); idx >= 0 && idx < s.length {
return s.getIdx(idx)
}
return s.baseObject.get(n)
}
func (s *stringObject) getStr(name string) Value {
if i := strToIdx(name); i >= 0 && i < s.length {
return s.getIdx(i)
}
return s.baseObject.getStr(name)
}
func (s *stringObject) getPropStr(name string) Value {
if i := strToIdx(name); i >= 0 && i < s.length {
return s.getIdx(i)
}
return s.baseObject.getPropStr(name)
}
func (s *stringObject) getProp(n Value) Value {
if i := toIdx(n); i >= 0 && i < s.length {
return s.getIdx(i)
}
return s.baseObject.getProp(n)
}
func (s *stringObject) getOwnProp(name string) Value {
if i := strToIdx(name); i >= 0 && i < s.length {
val := s.getIdx(i)
return &valueProperty{
value: val,
enumerable: true,
}
}
return s.baseObject.getOwnProp(name)
}
func (s *stringObject) getIdx(idx int64) Value {
return s.value.substring(idx, idx+1)
}
func (s *stringObject) put(n Value, val Value, throw bool) {
if i := toIdx(n); i >= 0 && i < s.length {
s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i)
return
}
s.baseObject.put(n, val, throw)
}
func (s *stringObject) putStr(name string, val Value, throw bool) {
if i := strToIdx(name); i >= 0 && i < s.length {
s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i)
return
}
s.baseObject.putStr(name, val, throw)
}
func (s *stringObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
if i := toIdx(n); i >= 0 && i < s.length {
s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
return false
}
return s.baseObject.defineOwnProperty(n, descr, throw)
}
type stringPropIter struct {
str valueString // separate, because obj can be the singleton
obj *stringObject
idx, length int64
recursive bool
}
func (i *stringPropIter) next() (propIterItem, iterNextFunc) {
if i.idx < i.length {
name := strconv.FormatInt(i.idx, 10)
i.idx++
return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.next
}
return i.obj.baseObject._enumerate(i.recursive)()
}
func (s *stringObject) _enumerate(recursive bool) iterNextFunc {
return (&stringPropIter{
str: s.value,
obj: s,
length: s.length,
recursive: recursive,
}).next
}
func (s *stringObject) enumerate(all, recursive bool) iterNextFunc {
return (&propFilterIter{
wrapped: s._enumerate(recursive),
all: all,
seen: make(map[string]bool),
}).next
}
func (s *stringObject) deleteStr(name string, throw bool) bool {
if i := strToIdx(name); i >= 0 && i < s.length {
s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i)
return false
}
return s.baseObject.deleteStr(name, throw)
}
func (s *stringObject) delete(n Value, throw bool) bool {
if i := toIdx(n); i >= 0 && i < s.length {
s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i)
return false
}
return s.baseObject.delete(n, throw)
}
func (s *stringObject) hasOwnProperty(n Value) bool {
if i := toIdx(n); i >= 0 && i < s.length {
return true
}
return s.baseObject.hasOwnProperty(n)
}
func (s *stringObject) hasOwnPropertyStr(name string) bool {
if i := strToIdx(name); i >= 0 && i < s.length {
return true
}
return s.baseObject.hasOwnPropertyStr(name)
}

313
vendor/github.com/dop251/goja/string_ascii.go generated vendored Normal file
View file

@ -0,0 +1,313 @@
package goja
import (
"fmt"
"io"
"math"
"reflect"
"strconv"
"strings"
)
type asciiString string
type asciiRuneReader struct {
s asciiString
pos int
}
func (rr *asciiRuneReader) ReadRune() (r rune, size int, err error) {
if rr.pos < len(rr.s) {
r = rune(rr.s[rr.pos])
size = 1
rr.pos++
} else {
err = io.EOF
}
return
}
func (s asciiString) reader(start int) io.RuneReader {
return &asciiRuneReader{
s: s[start:],
}
}
// ss must be trimmed
func strToInt(ss string) (int64, error) {
if ss == "" {
return 0, nil
}
if ss == "-0" {
return 0, strconv.ErrSyntax
}
if len(ss) > 2 {
switch ss[:2] {
case "0x", "0X":
i, _ := strconv.ParseInt(ss[2:], 16, 64)
return i, nil
case "0b", "0B":
i, _ := strconv.ParseInt(ss[2:], 2, 64)
return i, nil
case "0o", "0O":
i, _ := strconv.ParseInt(ss[2:], 8, 64)
return i, nil
}
}
return strconv.ParseInt(ss, 10, 64)
}
func (s asciiString) _toInt() (int64, error) {
return strToInt(strings.TrimSpace(string(s)))
}
func isRangeErr(err error) bool {
if err, ok := err.(*strconv.NumError); ok {
return err.Err == strconv.ErrRange
}
return false
}
func (s asciiString) _toFloat() (float64, error) {
ss := strings.TrimSpace(string(s))
if ss == "" {
return 0, nil
}
if ss == "-0" {
var f float64
return -f, nil
}
f, err := strconv.ParseFloat(ss, 64)
if isRangeErr(err) {
err = nil
}
return f, err
}
func (s asciiString) ToInteger() int64 {
if s == "" {
return 0
}
if s == "Infinity" || s == "+Infinity" {
return math.MaxInt64
}
if s == "-Infinity" {
return math.MinInt64
}
i, err := s._toInt()
if err != nil {
f, err := s._toFloat()
if err == nil {
return int64(f)
}
}
return i
}
func (s asciiString) ToString() valueString {
return s
}
func (s asciiString) String() string {
return string(s)
}
func (s asciiString) ToFloat() float64 {
if s == "" {
return 0
}
if s == "Infinity" || s == "+Infinity" {
return math.Inf(1)
}
if s == "-Infinity" {
return math.Inf(-1)
}
f, err := s._toFloat()
if err != nil {
i, err := s._toInt()
if err == nil {
return float64(i)
}
f = math.NaN()
}
return f
}
func (s asciiString) ToBoolean() bool {
return s != ""
}
func (s asciiString) ToNumber() Value {
if s == "" {
return intToValue(0)
}
if s == "Infinity" || s == "+Infinity" {
return _positiveInf
}
if s == "-Infinity" {
return _negativeInf
}
if i, err := s._toInt(); err == nil {
return intToValue(i)
}
if f, err := s._toFloat(); err == nil {
return floatToValue(f)
}
return _NaN
}
func (s asciiString) ToObject(r *Runtime) *Object {
return r._newString(s)
}
func (s asciiString) SameAs(other Value) bool {
if otherStr, ok := other.(asciiString); ok {
return s == otherStr
}
return false
}
func (s asciiString) Equals(other Value) bool {
if o, ok := other.(asciiString); ok {
return s == o
}
if o, ok := other.assertInt(); ok {
if o1, e := s._toInt(); e == nil {
return o1 == o
}
return false
}
if o, ok := other.assertFloat(); ok {
return s.ToFloat() == o
}
if o, ok := other.(valueBool); ok {
if o1, e := s._toFloat(); e == nil {
return o1 == o.ToFloat()
}
return false
}
if o, ok := other.(*Object); ok {
return s.Equals(o.self.toPrimitive())
}
return false
}
func (s asciiString) StrictEquals(other Value) bool {
if otherStr, ok := other.(asciiString); ok {
return s == otherStr
}
return false
}
func (s asciiString) assertInt() (int64, bool) {
return 0, false
}
func (s asciiString) assertFloat() (float64, bool) {
return 0, false
}
func (s asciiString) assertString() (valueString, bool) {
return s, true
}
func (s asciiString) baseObject(r *Runtime) *Object {
ss := r.stringSingleton
ss.value = s
ss.setLength()
return ss.val
}
func (s asciiString) charAt(idx int64) rune {
return rune(s[idx])
}
func (s asciiString) length() int64 {
return int64(len(s))
}
func (s asciiString) concat(other valueString) valueString {
switch other := other.(type) {
case asciiString:
b := make([]byte, len(s)+len(other))
copy(b, s)
copy(b[len(s):], other)
return asciiString(b)
//return asciiString(string(s) + string(other))
case unicodeString:
b := make([]uint16, len(s)+len(other))
for i := 0; i < len(s); i++ {
b[i] = uint16(s[i])
}
copy(b[len(s):], other)
return unicodeString(b)
default:
panic(fmt.Errorf("Unknown string type: %T", other))
}
}
func (s asciiString) substring(start, end int64) valueString {
return asciiString(s[start:end])
}
func (s asciiString) compareTo(other valueString) int {
switch other := other.(type) {
case asciiString:
return strings.Compare(string(s), string(other))
case unicodeString:
return strings.Compare(string(s), other.String())
default:
panic(fmt.Errorf("Unknown string type: %T", other))
}
}
func (s asciiString) index(substr valueString, start int64) int64 {
if substr, ok := substr.(asciiString); ok {
p := int64(strings.Index(string(s[start:]), string(substr)))
if p >= 0 {
return p + start
}
}
return -1
}
func (s asciiString) lastIndex(substr valueString, pos int64) int64 {
if substr, ok := substr.(asciiString); ok {
end := pos + int64(len(substr))
var ss string
if end > int64(len(s)) {
ss = string(s)
} else {
ss = string(s[:end])
}
return int64(strings.LastIndex(ss, string(substr)))
}
return -1
}
func (s asciiString) toLower() valueString {
return asciiString(strings.ToLower(string(s)))
}
func (s asciiString) toUpper() valueString {
return asciiString(strings.ToUpper(string(s)))
}
func (s asciiString) toTrimmedUTF8() string {
return strings.TrimSpace(string(s))
}
func (s asciiString) Export() interface{} {
return string(s)
}
func (s asciiString) ExportType() reflect.Type {
return reflectTypeString
}

319
vendor/github.com/dop251/goja/string_unicode.go generated vendored Normal file
View file

@ -0,0 +1,319 @@
package goja
import (
"errors"
"fmt"
"github.com/dop251/goja/parser"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"io"
"math"
"reflect"
"regexp"
"strings"
"unicode/utf16"
"unicode/utf8"
)
type unicodeString []uint16
type unicodeRuneReader struct {
s unicodeString
pos int
}
type runeReaderReplace struct {
wrapped io.RuneReader
}
var (
InvalidRuneError = errors.New("Invalid rune")
)
var (
unicodeTrimRegexp = regexp.MustCompile("^[" + parser.WhitespaceChars + "]*(.*?)[" + parser.WhitespaceChars + "]*$")
)
func (rr runeReaderReplace) ReadRune() (r rune, size int, err error) {
r, size, err = rr.wrapped.ReadRune()
if err == InvalidRuneError {
err = nil
r = utf8.RuneError
}
return
}
func (rr *unicodeRuneReader) ReadRune() (r rune, size int, err error) {
if rr.pos < len(rr.s) {
r = rune(rr.s[rr.pos])
if r != utf8.RuneError {
if utf16.IsSurrogate(r) {
if rr.pos+1 < len(rr.s) {
r1 := utf16.DecodeRune(r, rune(rr.s[rr.pos+1]))
size++
rr.pos++
if r1 == utf8.RuneError {
err = InvalidRuneError
} else {
r = r1
}
} else {
err = InvalidRuneError
}
}
}
size++
rr.pos++
} else {
err = io.EOF
}
return
}
func (s unicodeString) reader(start int) io.RuneReader {
return &unicodeRuneReader{
s: s[start:],
}
}
func (s unicodeString) ToInteger() int64 {
return 0
}
func (s unicodeString) ToString() valueString {
return s
}
func (s unicodeString) ToFloat() float64 {
return math.NaN()
}
func (s unicodeString) ToBoolean() bool {
return len(s) > 0
}
func (s unicodeString) toTrimmedUTF8() string {
if len(s) == 0 {
return ""
}
return unicodeTrimRegexp.FindStringSubmatch(s.String())[1]
}
func (s unicodeString) ToNumber() Value {
return asciiString(s.toTrimmedUTF8()).ToNumber()
}
func (s unicodeString) ToObject(r *Runtime) *Object {
return r._newString(s)
}
func (s unicodeString) equals(other unicodeString) bool {
if len(s) != len(other) {
return false
}
for i, r := range s {
if r != other[i] {
return false
}
}
return true
}
func (s unicodeString) SameAs(other Value) bool {
if otherStr, ok := other.(unicodeString); ok {
return s.equals(otherStr)
}
return false
}
func (s unicodeString) Equals(other Value) bool {
if s.SameAs(other) {
return true
}
if _, ok := other.assertInt(); ok {
return false
}
if _, ok := other.assertFloat(); ok {
return false
}
if _, ok := other.(valueBool); ok {
return false
}
if o, ok := other.(*Object); ok {
return s.Equals(o.self.toPrimitive())
}
return false
}
func (s unicodeString) StrictEquals(other Value) bool {
return s.SameAs(other)
}
func (s unicodeString) assertInt() (int64, bool) {
return 0, false
}
func (s unicodeString) assertFloat() (float64, bool) {
return 0, false
}
func (s unicodeString) assertString() (valueString, bool) {
return s, true
}
func (s unicodeString) baseObject(r *Runtime) *Object {
ss := r.stringSingleton
ss.value = s
ss.setLength()
return ss.val
}
func (s unicodeString) charAt(idx int64) rune {
return rune(s[idx])
}
func (s unicodeString) length() int64 {
return int64(len(s))
}
func (s unicodeString) concat(other valueString) valueString {
switch other := other.(type) {
case unicodeString:
return unicodeString(append(s, other...))
case asciiString:
b := make([]uint16, len(s)+len(other))
copy(b, s)
b1 := b[len(s):]
for i := 0; i < len(other); i++ {
b1[i] = uint16(other[i])
}
return unicodeString(b)
default:
panic(fmt.Errorf("Unknown string type: %T", other))
}
}
func (s unicodeString) substring(start, end int64) valueString {
ss := s[start:end]
for _, c := range ss {
if c >= utf8.RuneSelf {
return unicodeString(ss)
}
}
as := make([]byte, end-start)
for i, c := range ss {
as[i] = byte(c)
}
return asciiString(as)
}
func (s unicodeString) String() string {
return string(utf16.Decode(s))
}
func (s unicodeString) compareTo(other valueString) int {
return strings.Compare(s.String(), other.String())
}
func (s unicodeString) index(substr valueString, start int64) int64 {
var ss []uint16
switch substr := substr.(type) {
case unicodeString:
ss = substr
case asciiString:
ss = make([]uint16, len(substr))
for i := 0; i < len(substr); i++ {
ss[i] = uint16(substr[i])
}
default:
panic(fmt.Errorf("Unknown string type: %T", substr))
}
// TODO: optimise
end := int64(len(s) - len(ss))
for start <= end {
for i := int64(0); i < int64(len(ss)); i++ {
if s[start+i] != ss[i] {
goto nomatch
}
}
return start
nomatch:
start++
}
return -1
}
func (s unicodeString) lastIndex(substr valueString, start int64) int64 {
var ss []uint16
switch substr := substr.(type) {
case unicodeString:
ss = substr
case asciiString:
ss = make([]uint16, len(substr))
for i := 0; i < len(substr); i++ {
ss[i] = uint16(substr[i])
}
default:
panic(fmt.Errorf("Unknown string type: %T", substr))
}
if maxStart := int64(len(s) - len(ss)); start > maxStart {
start = maxStart
}
// TODO: optimise
for start >= 0 {
for i := int64(0); i < int64(len(ss)); i++ {
if s[start+i] != ss[i] {
goto nomatch
}
}
return start
nomatch:
start--
}
return -1
}
func (s unicodeString) toLower() valueString {
caser := cases.Lower(language.Und)
r := []rune(caser.String(s.String()))
// Workaround
ascii := true
for i := 0; i < len(r)-1; i++ {
if (i == 0 || r[i-1] != 0x3b1) && r[i] == 0x345 && r[i+1] == 0x3c2 {
i++
r[i] = 0x3c3
}
if r[i] >= utf8.RuneSelf {
ascii = false
}
}
if ascii {
ascii = r[len(r)-1] < utf8.RuneSelf
}
if ascii {
return asciiString(r)
}
return unicodeString(utf16.Encode(r))
}
func (s unicodeString) toUpper() valueString {
caser := cases.Upper(language.Und)
return newStringValue(caser.String(s.String()))
}
func (s unicodeString) Export() interface{} {
return s.String()
}
func (s unicodeString) ExportType() reflect.Type {
return reflectTypeString
}

862
vendor/github.com/dop251/goja/value.go generated vendored Normal file
View file

@ -0,0 +1,862 @@
package goja
import (
"math"
"reflect"
"regexp"
"strconv"
)
var (
valueFalse Value = valueBool(false)
valueTrue Value = valueBool(true)
_null Value = valueNull{}
_NaN Value = valueFloat(math.NaN())
_positiveInf Value = valueFloat(math.Inf(+1))
_negativeInf Value = valueFloat(math.Inf(-1))
_positiveZero Value
_negativeZero Value = valueFloat(math.Float64frombits(0 | (1 << 63)))
_epsilon = valueFloat(2.2204460492503130808472633361816e-16)
_undefined Value = valueUndefined{}
)
var (
reflectTypeInt = reflect.TypeOf(int64(0))
reflectTypeBool = reflect.TypeOf(false)
reflectTypeNil = reflect.TypeOf(nil)
reflectTypeFloat = reflect.TypeOf(float64(0))
reflectTypeMap = reflect.TypeOf(map[string]interface{}{})
reflectTypeArray = reflect.TypeOf([]interface{}{})
reflectTypeString = reflect.TypeOf("")
)
var intCache [256]Value
type Value interface {
ToInteger() int64
ToString() valueString
String() string
ToFloat() float64
ToNumber() Value
ToBoolean() bool
ToObject(*Runtime) *Object
SameAs(Value) bool
Equals(Value) bool
StrictEquals(Value) bool
Export() interface{}
ExportType() reflect.Type
assertInt() (int64, bool)
assertString() (valueString, bool)
assertFloat() (float64, bool)
baseObject(r *Runtime) *Object
}
type valueInt int64
type valueFloat float64
type valueBool bool
type valueNull struct{}
type valueUndefined struct {
valueNull
}
type valueUnresolved struct {
r *Runtime
ref string
}
type memberUnresolved struct {
valueUnresolved
}
type valueProperty struct {
value Value
writable bool
configurable bool
enumerable bool
accessor bool
getterFunc *Object
setterFunc *Object
}
func propGetter(o Value, v Value, r *Runtime) *Object {
if v == _undefined {
return nil
}
if obj, ok := v.(*Object); ok {
if _, ok := obj.self.assertCallable(); ok {
return obj
}
}
r.typeErrorResult(true, "Getter must be a function: %s", v.ToString())
return nil
}
func propSetter(o Value, v Value, r *Runtime) *Object {
if v == _undefined {
return nil
}
if obj, ok := v.(*Object); ok {
if _, ok := obj.self.assertCallable(); ok {
return obj
}
}
r.typeErrorResult(true, "Setter must be a function: %s", v.ToString())
return nil
}
func (i valueInt) ToInteger() int64 {
return int64(i)
}
func (i valueInt) ToString() valueString {
return asciiString(i.String())
}
func (i valueInt) String() string {
return strconv.FormatInt(int64(i), 10)
}
func (i valueInt) ToFloat() float64 {
return float64(int64(i))
}
func (i valueInt) ToBoolean() bool {
return i != 0
}
func (i valueInt) ToObject(r *Runtime) *Object {
return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber)
}
func (i valueInt) ToNumber() Value {
return i
}
func (i valueInt) SameAs(other Value) bool {
if otherInt, ok := other.assertInt(); ok {
return int64(i) == otherInt
}
return false
}
func (i valueInt) Equals(other Value) bool {
if o, ok := other.assertInt(); ok {
return int64(i) == o
}
if o, ok := other.assertFloat(); ok {
return float64(i) == o
}
if o, ok := other.assertString(); ok {
return o.ToNumber().Equals(i)
}
if o, ok := other.(valueBool); ok {
return int64(i) == o.ToInteger()
}
if o, ok := other.(*Object); ok {
return i.Equals(o.self.toPrimitiveNumber())
}
return false
}
func (i valueInt) StrictEquals(other Value) bool {
if otherInt, ok := other.assertInt(); ok {
return int64(i) == otherInt
} else if otherFloat, ok := other.assertFloat(); ok {
return float64(i) == otherFloat
}
return false
}
func (i valueInt) assertInt() (int64, bool) {
return int64(i), true
}
func (i valueInt) assertFloat() (float64, bool) {
return 0, false
}
func (i valueInt) assertString() (valueString, bool) {
return nil, false
}
func (i valueInt) baseObject(r *Runtime) *Object {
return r.global.NumberPrototype
}
func (i valueInt) Export() interface{} {
return int64(i)
}
func (i valueInt) ExportType() reflect.Type {
return reflectTypeInt
}
func (o valueBool) ToInteger() int64 {
if o {
return 1
}
return 0
}
func (o valueBool) ToString() valueString {
if o {
return stringTrue
}
return stringFalse
}
func (o valueBool) String() string {
if o {
return "true"
}
return "false"
}
func (o valueBool) ToFloat() float64 {
if o {
return 1.0
}
return 0
}
func (o valueBool) ToBoolean() bool {
return bool(o)
}
func (o valueBool) ToObject(r *Runtime) *Object {
return r.newPrimitiveObject(o, r.global.BooleanPrototype, "Boolean")
}
func (o valueBool) ToNumber() Value {
if o {
return valueInt(1)
}
return valueInt(0)
}
func (o valueBool) SameAs(other Value) bool {
if other, ok := other.(valueBool); ok {
return o == other
}
return false
}
func (b valueBool) Equals(other Value) bool {
if o, ok := other.(valueBool); ok {
return b == o
}
if b {
return other.Equals(intToValue(1))
} else {
return other.Equals(intToValue(0))
}
}
func (o valueBool) StrictEquals(other Value) bool {
if other, ok := other.(valueBool); ok {
return o == other
}
return false
}
func (o valueBool) assertInt() (int64, bool) {
return 0, false
}
func (o valueBool) assertFloat() (float64, bool) {
return 0, false
}
func (o valueBool) assertString() (valueString, bool) {
return nil, false
}
func (o valueBool) baseObject(r *Runtime) *Object {
return r.global.BooleanPrototype
}
func (o valueBool) Export() interface{} {
return bool(o)
}
func (o valueBool) ExportType() reflect.Type {
return reflectTypeBool
}
func (n valueNull) ToInteger() int64 {
return 0
}
func (n valueNull) ToString() valueString {
return stringNull
}
func (n valueNull) String() string {
return "null"
}
func (u valueUndefined) ToString() valueString {
return stringUndefined
}
func (u valueUndefined) String() string {
return "undefined"
}
func (u valueUndefined) ToNumber() Value {
return _NaN
}
func (u valueUndefined) SameAs(other Value) bool {
_, same := other.(valueUndefined)
return same
}
func (u valueUndefined) StrictEquals(other Value) bool {
_, same := other.(valueUndefined)
return same
}
func (u valueUndefined) ToFloat() float64 {
return math.NaN()
}
func (n valueNull) ToFloat() float64 {
return 0
}
func (n valueNull) ToBoolean() bool {
return false
}
func (n valueNull) ToObject(r *Runtime) *Object {
r.typeErrorResult(true, "Cannot convert undefined or null to object")
return nil
//return r.newObject()
}
func (n valueNull) ToNumber() Value {
return intToValue(0)
}
func (n valueNull) SameAs(other Value) bool {
_, same := other.(valueNull)
return same
}
func (n valueNull) Equals(other Value) bool {
switch other.(type) {
case valueUndefined, valueNull:
return true
}
return false
}
func (n valueNull) StrictEquals(other Value) bool {
_, same := other.(valueNull)
return same
}
func (n valueNull) assertInt() (int64, bool) {
return 0, false
}
func (n valueNull) assertFloat() (float64, bool) {
return 0, false
}
func (n valueNull) assertString() (valueString, bool) {
return nil, false
}
func (n valueNull) baseObject(r *Runtime) *Object {
return nil
}
func (n valueNull) Export() interface{} {
return nil
}
func (n valueNull) ExportType() reflect.Type {
return reflectTypeNil
}
func (p *valueProperty) ToInteger() int64 {
return 0
}
func (p *valueProperty) ToString() valueString {
return stringEmpty
}
func (p *valueProperty) String() string {
return ""
}
func (p *valueProperty) ToFloat() float64 {
return math.NaN()
}
func (p *valueProperty) ToBoolean() bool {
return false
}
func (p *valueProperty) ToObject(r *Runtime) *Object {
return nil
}
func (p *valueProperty) ToNumber() Value {
return nil
}
func (p *valueProperty) assertInt() (int64, bool) {
return 0, false
}
func (p *valueProperty) assertFloat() (float64, bool) {
return 0, false
}
func (p *valueProperty) assertString() (valueString, bool) {
return nil, false
}
func (p *valueProperty) isWritable() bool {
return p.writable || p.setterFunc != nil
}
func (p *valueProperty) get(this Value) Value {
if p.getterFunc == nil {
if p.value != nil {
return p.value
}
return _undefined
}
call, _ := p.getterFunc.self.assertCallable()
return call(FunctionCall{
This: this,
})
}
func (p *valueProperty) set(this, v Value) {
if p.setterFunc == nil {
p.value = v
return
}
call, _ := p.setterFunc.self.assertCallable()
call(FunctionCall{
This: this,
Arguments: []Value{v},
})
}
func (p *valueProperty) SameAs(other Value) bool {
if otherProp, ok := other.(*valueProperty); ok {
return p == otherProp
}
return false
}
func (p *valueProperty) Equals(other Value) bool {
return false
}
func (p *valueProperty) StrictEquals(other Value) bool {
return false
}
func (n *valueProperty) baseObject(r *Runtime) *Object {
r.typeErrorResult(true, "BUG: baseObject() is called on valueProperty") // TODO error message
return nil
}
func (n *valueProperty) Export() interface{} {
panic("Cannot export valueProperty")
}
func (n *valueProperty) ExportType() reflect.Type {
panic("Cannot export valueProperty")
}
func (f valueFloat) ToInteger() int64 {
switch {
case math.IsNaN(float64(f)):
return 0
case math.IsInf(float64(f), 1):
return int64(math.MaxInt64)
case math.IsInf(float64(f), -1):
return int64(math.MinInt64)
}
return int64(f)
}
func (f valueFloat) ToString() valueString {
return asciiString(f.String())
}
var matchLeading0Exponent = regexp.MustCompile(`([eE][\+\-])0+([1-9])`) // 1e-07 => 1e-7
func (f valueFloat) String() string {
value := float64(f)
if math.IsNaN(value) {
return "NaN"
} else if math.IsInf(value, 0) {
if math.Signbit(value) {
return "-Infinity"
}
return "Infinity"
} else if f == _negativeZero {
return "0"
}
exponent := math.Log10(math.Abs(value))
if exponent >= 21 || exponent < -6 {
return matchLeading0Exponent.ReplaceAllString(strconv.FormatFloat(value, 'g', -1, 64), "$1$2")
}
return strconv.FormatFloat(value, 'f', -1, 64)
}
func (f valueFloat) ToFloat() float64 {
return float64(f)
}
func (f valueFloat) ToBoolean() bool {
return float64(f) != 0.0 && !math.IsNaN(float64(f))
}
func (f valueFloat) ToObject(r *Runtime) *Object {
return r.newPrimitiveObject(f, r.global.NumberPrototype, "Number")
}
func (f valueFloat) ToNumber() Value {
return f
}
func (f valueFloat) SameAs(other Value) bool {
if o, ok := other.assertFloat(); ok {
this := float64(f)
if math.IsNaN(this) && math.IsNaN(o) {
return true
} else {
ret := this == o
if ret && this == 0 {
ret = math.Signbit(this) == math.Signbit(o)
}
return ret
}
} else if o, ok := other.assertInt(); ok {
this := float64(f)
ret := this == float64(o)
if ret && this == 0 {
ret = !math.Signbit(this)
}
return ret
}
return false
}
func (f valueFloat) Equals(other Value) bool {
if o, ok := other.assertFloat(); ok {
return float64(f) == o
}
if o, ok := other.assertInt(); ok {
return float64(f) == float64(o)
}
if _, ok := other.assertString(); ok {
return float64(f) == other.ToFloat()
}
if o, ok := other.(valueBool); ok {
return float64(f) == o.ToFloat()
}
if o, ok := other.(*Object); ok {
return f.Equals(o.self.toPrimitiveNumber())
}
return false
}
func (f valueFloat) StrictEquals(other Value) bool {
if o, ok := other.assertFloat(); ok {
return float64(f) == o
} else if o, ok := other.assertInt(); ok {
return float64(f) == float64(o)
}
return false
}
func (f valueFloat) assertInt() (int64, bool) {
return 0, false
}
func (f valueFloat) assertFloat() (float64, bool) {
return float64(f), true
}
func (f valueFloat) assertString() (valueString, bool) {
return nil, false
}
func (f valueFloat) baseObject(r *Runtime) *Object {
return r.global.NumberPrototype
}
func (f valueFloat) Export() interface{} {
return float64(f)
}
func (f valueFloat) ExportType() reflect.Type {
return reflectTypeFloat
}
func (o *Object) ToInteger() int64 {
return o.self.toPrimitiveNumber().ToNumber().ToInteger()
}
func (o *Object) ToString() valueString {
return o.self.toPrimitiveString().ToString()
}
func (o *Object) String() string {
return o.self.toPrimitiveString().String()
}
func (o *Object) ToFloat() float64 {
return o.self.toPrimitiveNumber().ToFloat()
}
func (o *Object) ToBoolean() bool {
return true
}
func (o *Object) ToObject(r *Runtime) *Object {
return o
}
func (o *Object) ToNumber() Value {
return o.self.toPrimitiveNumber().ToNumber()
}
func (o *Object) SameAs(other Value) bool {
if other, ok := other.(*Object); ok {
return o == other
}
return false
}
func (o *Object) Equals(other Value) bool {
if other, ok := other.(*Object); ok {
return o == other || o.self.equal(other.self)
}
if _, ok := other.assertInt(); ok {
return o.self.toPrimitive().Equals(other)
}
if _, ok := other.assertFloat(); ok {
return o.self.toPrimitive().Equals(other)
}
if other, ok := other.(valueBool); ok {
return o.Equals(other.ToNumber())
}
if _, ok := other.assertString(); ok {
return o.self.toPrimitive().Equals(other)
}
return false
}
func (o *Object) StrictEquals(other Value) bool {
if other, ok := other.(*Object); ok {
return o == other || o.self.equal(other.self)
}
return false
}
func (o *Object) assertInt() (int64, bool) {
return 0, false
}
func (o *Object) assertFloat() (float64, bool) {
return 0, false
}
func (o *Object) assertString() (valueString, bool) {
return nil, false
}
func (o *Object) baseObject(r *Runtime) *Object {
return o
}
func (o *Object) Export() interface{} {
return o.self.export()
}
func (o *Object) ExportType() reflect.Type {
return o.self.exportType()
}
func (o *Object) Get(name string) Value {
return o.self.getStr(name)
}
func (o *Object) Keys() (keys []string) {
for item, f := o.self.enumerate(false, false)(); f != nil; item, f = f() {
keys = append(keys, item.name)
}
return
}
// DefineDataProperty is a Go equivalent of Object.defineProperty(o, name, {value: value, writable: writable,
// configurable: configurable, enumerable: enumerable})
func (o *Object) DefineDataProperty(name string, value Value, writable, configurable, enumerable Flag) error {
return tryFunc(func() {
o.self.defineOwnProperty(newStringValue(name), propertyDescr{
Value: value,
Writable: writable,
Configurable: configurable,
Enumerable: enumerable,
}, true)
})
}
// DefineAccessorProperty is a Go equivalent of Object.defineProperty(o, name, {get: getter, set: setter,
// configurable: configurable, enumerable: enumerable})
func (o *Object) DefineAccessorProperty(name string, getter, setter Value, configurable, enumerable Flag) error {
return tryFunc(func() {
o.self.defineOwnProperty(newStringValue(name), propertyDescr{
Getter: getter,
Setter: setter,
Configurable: configurable,
Enumerable: enumerable,
}, true)
})
}
func (o *Object) Set(name string, value interface{}) error {
return tryFunc(func() {
o.self.putStr(name, o.runtime.ToValue(value), true)
})
}
// MarshalJSON returns JSON representation of the Object. It is equivalent to JSON.stringify(o).
// Note, this implements json.Marshaler so that json.Marshal() can be used without the need to Export().
func (o *Object) MarshalJSON() ([]byte, error) {
ctx := _builtinJSON_stringifyContext{
r: o.runtime,
}
ex := o.runtime.vm.try(func() {
if !ctx.do(o) {
ctx.buf.WriteString("null")
}
})
if ex != nil {
return nil, ex
}
return ctx.buf.Bytes(), nil
}
// ClassName returns the class name
func (o *Object) ClassName() string {
return o.self.className()
}
func (o valueUnresolved) throw() {
o.r.throwReferenceError(o.ref)
}
func (o valueUnresolved) ToInteger() int64 {
o.throw()
return 0
}
func (o valueUnresolved) ToString() valueString {
o.throw()
return nil
}
func (o valueUnresolved) String() string {
o.throw()
return ""
}
func (o valueUnresolved) ToFloat() float64 {
o.throw()
return 0
}
func (o valueUnresolved) ToBoolean() bool {
o.throw()
return false
}
func (o valueUnresolved) ToObject(r *Runtime) *Object {
o.throw()
return nil
}
func (o valueUnresolved) ToNumber() Value {
o.throw()
return nil
}
func (o valueUnresolved) SameAs(other Value) bool {
o.throw()
return false
}
func (o valueUnresolved) Equals(other Value) bool {
o.throw()
return false
}
func (o valueUnresolved) StrictEquals(other Value) bool {
o.throw()
return false
}
func (o valueUnresolved) assertInt() (int64, bool) {
o.throw()
return 0, false
}
func (o valueUnresolved) assertFloat() (float64, bool) {
o.throw()
return 0, false
}
func (o valueUnresolved) assertString() (valueString, bool) {
o.throw()
return nil, false
}
func (o valueUnresolved) baseObject(r *Runtime) *Object {
o.throw()
return nil
}
func (o valueUnresolved) Export() interface{} {
o.throw()
return nil
}
func (o valueUnresolved) ExportType() reflect.Type {
o.throw()
return nil
}
func init() {
for i := 0; i < 256; i++ {
intCache[i] = valueInt(i - 128)
}
_positiveZero = intToValue(0)
}

2521
vendor/github.com/dop251/goja/vm.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.)

View file

@ -1,7 +0,0 @@
Copyright (c) 2012 Robert Krimen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,63 +0,0 @@
.PHONY: test test-race test-release release release-check test-262
.PHONY: parser
.PHONY: otto assets underscore
TESTS := \
~
TEST := -v --run
TEST := -v
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
TEST := .
test: parser inline.go
go test -i
go test $(TEST)
@echo PASS
parser:
$(MAKE) -C parser
inline.go: inline.pl
./$< > $@
#################
# release, test #
#################
release: test-race test-release
for package in . parser token ast file underscore registry; do (cd $$package && godocdown --signature > README.markdown); done
@echo \*\*\* make release-check
@echo PASS
release-check: .test
$(MAKE) -C test build test
$(MAKE) -C .test/test262 build test
@echo PASS
test-262: .test
$(MAKE) -C .test/test262 build test
@echo PASS
test-release:
go test -i
go test
test-race:
go test -race -i
go test -race
#################################
# otto, assets, underscore, ... #
#################################
otto:
$(MAKE) -C otto
assets:
mkdir -p .assets
for file in underscore/test/*.js; do tr "\`" "_" < $$file > .assets/`basename $$file`; done
underscore:
$(MAKE) -C $@

View file

@ -1,871 +0,0 @@
# otto
--
```go
import "github.com/robertkrimen/otto"
```
Package otto is a JavaScript parser and interpreter written natively in Go.
http://godoc.org/github.com/robertkrimen/otto
```go
import (
"github.com/robertkrimen/otto"
)
```
Run something in the VM
```go
vm := otto.New()
vm.Run(`
abc = 2 + 2;
console.log("The value of abc is " + abc); // 4
`)
```
Get a value out of the VM
```go
if value, err := vm.Get("abc"); err == nil {
if value_int, err := value.ToInteger(); err == nil {
fmt.Printf("", value_int, err)
}
}
```
Set a number
```go
vm.Set("def", 11)
vm.Run(`
console.log("The value of def is " + def);
// The value of def is 11
`)
```
Set a string
```go
vm.Set("xyzzy", "Nothing happens.")
vm.Run(`
console.log(xyzzy.length); // 16
`)
```
Get the value of an expression
```go
value, _ = vm.Run("xyzzy.length")
{
// value is an int64 with a value of 16
value, _ := value.ToInteger()
}
```
An error happens
```go
value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length")
if err != nil {
// err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
// If there is an error, then value.IsUndefined() is true
...
}
```
Set a Go function
```go
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
return otto.Value{}
})
```
Set a Go function that returns something useful
```go
vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
right, _ := call.Argument(0).ToInteger()
result, _ := vm.ToValue(2 + right)
return result
})
```
Use the functions in JavaScript
```go
result, _ = vm.Run(`
sayHello("Xyzzy"); // Hello, Xyzzy.
sayHello(); // Hello, undefined
result = twoPlus(2.0); // 4
`)
```
### Parser
A separate parser is available in the parser package if you're just interested
in building an AST.
http://godoc.org/github.com/robertkrimen/otto/parser
Parse and return an AST
```go
filename := "" // A filename is optional
src := `
// Sample xyzzy example
(function(){
if (3.14159 > 0) {
console.log("Hello, World.");
return;
}
var xyzzy = NaN;
console.log("Nothing happens.");
return xyzzy;
})();
`
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, filename, src, 0)
```
### otto
You can run (Go) JavaScript from the commandline with:
http://github.com/robertkrimen/otto/tree/master/otto
$ go get -v github.com/robertkrimen/otto/otto
Run JavaScript by entering some source on stdin or by giving otto a filename:
$ otto example.js
### underscore
Optionally include the JavaScript utility-belt library, underscore, with this
import:
```go
import (
"github.com/robertkrimen/otto"
_ "github.com/robertkrimen/otto/underscore"
)
// Now every otto runtime will come loaded with underscore
```
For more information: http://github.com/robertkrimen/otto/tree/master/underscore
### Caveat Emptor
The following are some limitations with otto:
* "use strict" will parse, but does nothing.
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported.
### Regular Expression Incompatibility
Go translates JavaScript-style regular expressions into something that is
"regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires
backtracking for some patterns, and backtracking is not supported by the
standard Go engine: https://code.google.com/p/re2/wiki/Syntax
Therefore, the following syntax is incompatible:
(?=) // Lookahead (positive), currently a parsing error
(?!) // Lookahead (backhead), currently a parsing error
\1 // Backreference (\1, \2, \3, ...), currently a parsing error
A brief discussion of these limitations: "Regexp (?!re)"
https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E
More information about re2: https://code.google.com/p/re2/
In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r
]. The JavaScript definition, on the other hand, also includes \v, Unicode
"Separator, Space", etc.
### Halting Problem
If you want to stop long running executions (like third-party code), you can use
the interrupt channel to do this:
```go
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/robertkrimen/otto"
)
var halt = errors.New("Stahp")
func main() {
runUnsafe(`var abc = [];`)
runUnsafe(`
while (true) {
// Loop forever
}`)
}
func runUnsafe(unsafe string) {
start := time.Now()
defer func() {
duration := time.Since(start)
if caught := recover(); caught != nil {
if caught == halt {
fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration)
return
}
panic(caught) // Something else happened, repanic!
}
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
}()
vm := otto.New()
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
go func() {
time.Sleep(2 * time.Second) // Stop after two seconds
vm.Interrupt <- func() {
panic(halt)
}
}()
vm.Run(unsafe) // Here be dragons (risky code)
}
```
Where is setTimeout/setInterval?
These timing functions are not actually part of the ECMA-262 specification.
Typically, they belong to the `window` object (in the browser). It would not be
difficult to provide something like these via Go, but you probably want to wrap
otto in an event loop in that case.
For an example of how this could be done in Go with otto, see natto:
http://github.com/robertkrimen/natto
Here is some more discussion of the issue:
* http://book.mixu.net/node/ch2.html
* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29
* http://aaroncrane.co.uk/2009/02/perl_safe_signals/
## Usage
```go
var ErrVersion = errors.New("version mismatch")
```
#### type Error
```go
type Error struct {
}
```
An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
#### func (Error) Error
```go
func (err Error) Error() string
```
Error returns a description of the error
TypeError: 'def' is not a function
#### func (Error) String
```go
func (err Error) String() string
```
String returns a description of the error and a trace of where the error
occurred.
TypeError: 'def' is not a function
at xyz (<anonymous>:3:9)
at <anonymous>:7:1/
#### type FunctionCall
```go
type FunctionCall struct {
This Value
ArgumentList []Value
Otto *Otto
}
```
FunctionCall is an encapsulation of a JavaScript function call.
#### func (FunctionCall) Argument
```go
func (self FunctionCall) Argument(index int) Value
```
Argument will return the value of the argument at the given index.
If no such argument exists, undefined is returned.
#### type Object
```go
type Object struct {
}
```
Object is the representation of a JavaScript object.
#### func (Object) Call
```go
func (self Object) Call(name string, argumentList ...interface{}) (Value, error)
```
Call a method on the object.
It is essentially equivalent to:
var method, _ := object.Get(name)
method.Call(object, argumentList...)
An undefined value and an error will result if:
1. There is an error during conversion of the argument list
2. The property is not actually a function
3. An (uncaught) exception is thrown
#### func (Object) Class
```go
func (self Object) Class() string
```
Class will return the class string of the object.
The return value will (generally) be one of:
Object
Function
Array
String
Number
Boolean
Date
RegExp
#### func (Object) Get
```go
func (self Object) Get(name string) (Value, error)
```
Get the value of the property with the given name.
#### func (Object) Keys
```go
func (self Object) Keys() []string
```
Get the keys for the object
Equivalent to calling Object.keys on the object
#### func (Object) Set
```go
func (self Object) Set(name string, value interface{}) error
```
Set the property of the given name to the given value.
An error will result if the setting the property triggers an exception (i.e.
read-only), or there is an error during conversion of the given value.
#### func (Object) Value
```go
func (self Object) Value() Value
```
Value will return self as a value.
#### type Otto
```go
type Otto struct {
// Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example.
// See "Halting Problem" for more information.
Interrupt chan func()
}
```
Otto is the representation of the JavaScript runtime. Each instance of Otto has
a self-contained namespace.
#### func New
```go
func New() *Otto
```
New will allocate a new JavaScript runtime
#### func Run
```go
func Run(src interface{}) (*Otto, Value, error)
```
Run will allocate a new JavaScript runtime, run the given source on the
allocated runtime, and return the runtime, resulting value, and error (if any).
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
always be in UTF-8.
src may also be a Script.
src may also be a Program, but if the AST has been modified, then runtime
behavior is undefined.
#### func (Otto) Call
```go
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error)
```
Call the given JavaScript with a given this and arguments.
If this is nil, then some special handling takes place to determine the proper
this value, falling back to a "standard" invocation if necessary (where this is
undefined).
If source begins with "new " (A lowercase new followed by a space), then Call
will invoke the function constructor rather than performing a function call. In
this case, the this argument has no effect.
```go
// value is a String object
value, _ := vm.Call("Object", nil, "Hello, World.")
// Likewise...
value, _ := vm.Call("new Object", nil, "Hello, World.")
// This will perform a concat on the given array and return the result
// value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ]
value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc")
```
#### func (*Otto) Compile
```go
func (self *Otto) Compile(filename string, src interface{}) (*Script, error)
```
Compile will parse the given source and return a Script value or nil and an
error if there was a problem during compilation.
```go
script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
vm.Run(script)
```
#### func (*Otto) Copy
```go
func (in *Otto) Copy() *Otto
```
Copy will create a copy/clone of the runtime.
Copy is useful for saving some time when creating many similar runtimes.
This method works by walking the original runtime and cloning each object,
scope, stash, etc. into a new runtime.
Be on the lookout for memory leaks or inadvertent sharing of resources.
#### func (Otto) Get
```go
func (self Otto) Get(name string) (Value, error)
```
Get the value of the top-level binding of the given name.
If there is an error (like the binding does not exist), then the value will be
undefined.
#### func (Otto) Object
```go
func (self Otto) Object(source string) (*Object, error)
```
Object will run the given source and return the result as an object.
For example, accessing an existing object:
```go
object, _ := vm.Object(`Number`)
```
Or, creating a new object:
```go
object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`)
```
Or, creating and assigning an object:
```go
object, _ := vm.Object(`xyzzy = {}`)
object.Set("volume", 11)
```
If there is an error (like the source does not result in an object), then nil
and an error is returned.
#### func (Otto) Run
```go
func (self Otto) Run(src interface{}) (Value, error)
```
Run will run the given source (parsing it first if necessary), returning the
resulting value and error (if any)
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
always be in UTF-8.
If the runtime is unable to parse source, then this function will return
undefined and the parse error (nothing will be evaluated in this case).
src may also be a Script.
src may also be a Program, but if the AST has been modified, then runtime
behavior is undefined.
#### func (Otto) Set
```go
func (self Otto) Set(name string, value interface{}) error
```
Set the top-level binding of the given name to the given value.
Set will automatically apply ToValue to the given value in order to convert it
to a JavaScript value (type Value).
If there is an error (like the binding is read-only, or the ToValue conversion
fails), then an error is returned.
If the top-level binding does not exist, it will be created.
#### func (Otto) ToValue
```go
func (self Otto) ToValue(value interface{}) (Value, error)
```
ToValue will convert an interface{} value to a value digestible by
otto/JavaScript.
#### type Script
```go
type Script struct {
}
```
Script is a handle for some (reusable) JavaScript. Passing a Script value to a
run method will evaluate the JavaScript.
#### func (*Script) String
```go
func (self *Script) String() string
```
#### type Value
```go
type Value struct {
}
```
Value is the representation of a JavaScript value.
#### func FalseValue
```go
func FalseValue() Value
```
FalseValue will return a value representing false.
It is equivalent to:
```go
ToValue(false)
```
#### func NaNValue
```go
func NaNValue() Value
```
NaNValue will return a value representing NaN.
It is equivalent to:
```go
ToValue(math.NaN())
```
#### func NullValue
```go
func NullValue() Value
```
NullValue will return a Value representing null.
#### func ToValue
```go
func ToValue(value interface{}) (Value, error)
```
ToValue will convert an interface{} value to a value digestible by
otto/JavaScript
This function will not work for advanced types (struct, map, slice/array, etc.)
and you should use Otto.ToValue instead.
#### func TrueValue
```go
func TrueValue() Value
```
TrueValue will return a value representing true.
It is equivalent to:
```go
ToValue(true)
```
#### func UndefinedValue
```go
func UndefinedValue() Value
```
UndefinedValue will return a Value representing undefined.
#### func (Value) Call
```go
func (value Value) Call(this Value, argumentList ...interface{}) (Value, error)
```
Call the value as a function with the given this value and argument list and
return the result of invocation. It is essentially equivalent to:
value.apply(thisValue, argumentList)
An undefined value and an error will result if:
1. There is an error during conversion of the argument list
2. The value is not actually a function
3. An (uncaught) exception is thrown
#### func (Value) Class
```go
func (value Value) Class() string
```
Class will return the class string of the value or the empty string if value is
not an object.
The return value will (generally) be one of:
Object
Function
Array
String
Number
Boolean
Date
RegExp
#### func (Value) Export
```go
func (self Value) Export() (interface{}, error)
```
Export will attempt to convert the value to a Go representation and return it
via an interface{} kind.
Export returns an error, but it will always be nil. It is present for backwards
compatibility.
If a reasonable conversion is not possible, then the original value is returned.
undefined -> nil (FIXME?: Should be Value{})
null -> nil
boolean -> bool
number -> A number type (int, float32, uint64, ...)
string -> string
Array -> []interface{}
Object -> map[string]interface{}
#### func (Value) IsBoolean
```go
func (value Value) IsBoolean() bool
```
IsBoolean will return true if value is a boolean (primitive).
#### func (Value) IsDefined
```go
func (value Value) IsDefined() bool
```
IsDefined will return false if the value is undefined, and true otherwise.
#### func (Value) IsFunction
```go
func (value Value) IsFunction() bool
```
IsFunction will return true if value is a function.
#### func (Value) IsNaN
```go
func (value Value) IsNaN() bool
```
IsNaN will return true if value is NaN (or would convert to NaN).
#### func (Value) IsNull
```go
func (value Value) IsNull() bool
```
IsNull will return true if the value is null, and false otherwise.
#### func (Value) IsNumber
```go
func (value Value) IsNumber() bool
```
IsNumber will return true if value is a number (primitive).
#### func (Value) IsObject
```go
func (value Value) IsObject() bool
```
IsObject will return true if value is an object.
#### func (Value) IsPrimitive
```go
func (value Value) IsPrimitive() bool
```
IsPrimitive will return true if value is a primitive (any kind of primitive).
#### func (Value) IsString
```go
func (value Value) IsString() bool
```
IsString will return true if value is a string (primitive).
#### func (Value) IsUndefined
```go
func (value Value) IsUndefined() bool
```
IsUndefined will return true if the value is undefined, and false otherwise.
#### func (Value) Object
```go
func (value Value) Object() *Object
```
Object will return the object of the value, or nil if value is not an object.
This method will not do any implicit conversion. For example, calling this
method on a string primitive value will not return a String object.
#### func (Value) String
```go
func (value Value) String() string
```
String will return the value as a string.
This method will make return the empty string if there is an error.
#### func (Value) ToBoolean
```go
func (value Value) ToBoolean() (bool, error)
```
ToBoolean will convert the value to a boolean (bool).
ToValue(0).ToBoolean() => false
ToValue("").ToBoolean() => false
ToValue(true).ToBoolean() => true
ToValue(1).ToBoolean() => true
ToValue("Nothing happens").ToBoolean() => true
If there is an error during the conversion process (like an uncaught exception),
then the result will be false and an error.
#### func (Value) ToFloat
```go
func (value Value) ToFloat() (float64, error)
```
ToFloat will convert the value to a number (float64).
ToValue(0).ToFloat() => 0.
ToValue(1.1).ToFloat() => 1.1
ToValue("11").ToFloat() => 11.
If there is an error during the conversion process (like an uncaught exception),
then the result will be 0 and an error.
#### func (Value) ToInteger
```go
func (value Value) ToInteger() (int64, error)
```
ToInteger will convert the value to a number (int64).
ToValue(0).ToInteger() => 0
ToValue(1.1).ToInteger() => 1
ToValue("11").ToInteger() => 11
If there is an error during the conversion process (like an uncaught exception),
then the result will be 0 and an error.
#### func (Value) ToString
```go
func (value Value) ToString() (string, error)
```
ToString will convert the value to a string (string).
ToValue(0).ToString() => "0"
ToValue(false).ToString() => "false"
ToValue(1.1).ToString() => "1.1"
ToValue("11").ToString() => "11"
ToValue('Nothing happens.').ToString() => "Nothing happens."
If there is an error during the conversion process (like an uncaught exception),
then the result will be the empty string ("") and an error.
--
**godocdown** http://github.com/robertkrimen/godocdown

View file

@ -1,354 +0,0 @@
package otto
import (
"encoding/hex"
"math"
"net/url"
"regexp"
"strconv"
"strings"
"unicode/utf16"
"unicode/utf8"
)
// Global
func builtinGlobal_eval(call FunctionCall) Value {
src := call.Argument(0)
if !src.IsString() {
return src
}
runtime := call.runtime
program := runtime.cmpl_parseOrThrow(src.string(), nil)
if !call.eval {
// Not a direct call to eval, so we enter the global ExecutionContext
runtime.enterGlobalScope()
defer runtime.leaveScope()
}
returnValue := runtime.cmpl_evaluate_nodeProgram(program, true)
if returnValue.isEmpty() {
return Value{}
}
return returnValue
}
func builtinGlobal_isNaN(call FunctionCall) Value {
value := call.Argument(0).float64()
return toValue_bool(math.IsNaN(value))
}
func builtinGlobal_isFinite(call FunctionCall) Value {
value := call.Argument(0).float64()
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
}
// radix 3 => 2 (ASCII 50) +47
// radix 11 => A/a (ASCII 65/97) +54/+86
var parseInt_alphabetTable = func() []string {
table := []string{"", "", "01"}
for radix := 3; radix <= 36; radix += 1 {
alphabet := table[radix-1]
if radix <= 10 {
alphabet += string(radix + 47)
} else {
alphabet += string(radix+54) + string(radix+86)
}
table = append(table, alphabet)
}
return table
}()
func digitValue(chr rune) int {
switch {
case '0' <= chr && chr <= '9':
return int(chr - '0')
case 'a' <= chr && chr <= 'z':
return int(chr - 'a' + 10)
case 'A' <= chr && chr <= 'Z':
return int(chr - 'A' + 10)
}
return 36 // Larger than any legal digit value
}
func builtinGlobal_parseInt(call FunctionCall) Value {
input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
if len(input) == 0 {
return NaNValue()
}
radix := int(toInt32(call.Argument(1)))
negative := false
switch input[0] {
case '+':
input = input[1:]
case '-':
negative = true
input = input[1:]
}
strip := true
if radix == 0 {
radix = 10
} else {
if radix < 2 || radix > 36 {
return NaNValue()
} else if radix != 16 {
strip = false
}
}
switch len(input) {
case 0:
return NaNValue()
case 1:
default:
if strip {
if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
input = input[2:]
radix = 16
}
}
}
base := radix
index := 0
for ; index < len(input); index++ {
digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway
if digit >= base {
break
}
}
input = input[0:index]
value, err := strconv.ParseInt(input, radix, 64)
if err != nil {
if err.(*strconv.NumError).Err == strconv.ErrRange {
base := float64(base)
// Could just be a very large number (e.g. 0x8000000000000000)
var value float64
for _, chr := range input {
digit := float64(digitValue(chr))
if digit >= base {
goto error
}
value = value*base + digit
}
if negative {
value *= -1
}
return toValue_float64(value)
}
error:
return NaNValue()
}
if negative {
value *= -1
}
return toValue_int64(value)
}
var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`)
var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`)
func builtinGlobal_parseFloat(call FunctionCall) Value {
// Caveat emptor: This implementation does NOT match the specification
input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
if parseFloat_matchBadSpecial.MatchString(input) {
return NaNValue()
}
value, err := strconv.ParseFloat(input, 64)
if err != nil {
for end := len(input); end > 0; end-- {
input := input[0:end]
if !parseFloat_matchValid.MatchString(input) {
return NaNValue()
}
value, err = strconv.ParseFloat(input, 64)
if err == nil {
break
}
}
if err != nil {
return NaNValue()
}
}
return toValue_float64(value)
}
// encodeURI/decodeURI
func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
value := call.Argument(0)
var input []uint16
switch vl := value.value.(type) {
case []uint16:
input = vl
default:
input = utf16.Encode([]rune(value.string()))
}
if len(input) == 0 {
return toValue_string("")
}
output := []byte{}
length := len(input)
encode := make([]byte, 4)
for index := 0; index < length; {
value := input[index]
decode := utf16.Decode(input[index : index+1])
if value >= 0xDC00 && value <= 0xDFFF {
panic(call.runtime.panicURIError("URI malformed"))
}
if value >= 0xD800 && value <= 0xDBFF {
index += 1
if index >= length {
panic(call.runtime.panicURIError("URI malformed"))
}
// input = ..., value, value1, ...
value1 := input[index]
if value1 < 0xDC00 || value1 > 0xDFFF {
panic(call.runtime.panicURIError("URI malformed"))
}
decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
}
index += 1
size := utf8.EncodeRune(encode, decode[0])
encode := encode[0:size]
output = append(output, encode...)
}
{
value := escape.ReplaceAllFunc(output, func(target []byte) []byte {
// Probably a better way of doing this
if target[0] == ' ' {
return []byte("%20")
}
return []byte(url.QueryEscape(string(target)))
})
return toValue_string(string(value))
}
}
var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`)
func builtinGlobal_encodeURI(call FunctionCall) Value {
return _builtinGlobal_encodeURI(call, encodeURI_Regexp)
}
var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`)
func builtinGlobal_encodeURIComponent(call FunctionCall) Value {
return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp)
}
// 3B/2F/3F/3A/40/26/3D/2B/24/2C/23
var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`)
func _decodeURI(input string, reserve bool) (string, bool) {
if reserve {
input = decodeURI_guard.ReplaceAllString(input, "%25$1")
}
input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case
output, err := url.QueryUnescape(input)
if err != nil || !utf8.ValidString(output) {
return "", true
}
return output, false
}
func builtinGlobal_decodeURI(call FunctionCall) Value {
output, err := _decodeURI(call.Argument(0).string(), true)
if err {
panic(call.runtime.panicURIError("URI malformed"))
}
return toValue_string(output)
}
func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
output, err := _decodeURI(call.Argument(0).string(), false)
if err {
panic(call.runtime.panicURIError("URI malformed"))
}
return toValue_string(output)
}
// escape/unescape
func builtin_shouldEscape(chr byte) bool {
if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' {
return false
}
return !strings.ContainsRune("*_+-./", rune(chr))
}
const escapeBase16 = "0123456789ABCDEF"
func builtin_escape(input string) string {
output := make([]byte, 0, len(input))
length := len(input)
for index := 0; index < length; {
if builtin_shouldEscape(input[index]) {
chr, width := utf8.DecodeRuneInString(input[index:])
chr16 := utf16.Encode([]rune{chr})[0]
if 256 > chr16 {
output = append(output, '%',
escapeBase16[chr16>>4],
escapeBase16[chr16&15],
)
} else {
output = append(output, '%', 'u',
escapeBase16[chr16>>12],
escapeBase16[(chr16>>8)&15],
escapeBase16[(chr16>>4)&15],
escapeBase16[chr16&15],
)
}
index += width
} else {
output = append(output, input[index])
index += 1
}
}
return string(output)
}
func builtin_unescape(input string) string {
output := make([]rune, 0, len(input))
length := len(input)
for index := 0; index < length; {
if input[index] == '%' {
if index <= length-6 && input[index+1] == 'u' {
byte16, err := hex.DecodeString(input[index+2 : index+6])
if err == nil {
value := uint16(byte16[0])<<8 + uint16(byte16[1])
chr := utf16.Decode([]uint16{value})[0]
output = append(output, chr)
index += 6
continue
}
}
if index <= length-3 {
byte8, err := hex.DecodeString(input[index+1 : index+3])
if err == nil {
value := uint16(byte8[0])
chr := utf16.Decode([]uint16{value})[0]
output = append(output, chr)
index += 3
continue
}
}
}
output = append(output, rune(input[index]))
index += 1
}
return string(output)
}
func builtinGlobal_escape(call FunctionCall) Value {
return toValue_string(builtin_escape(call.Argument(0).string()))
}
func builtinGlobal_unescape(call FunctionCall) Value {
return toValue_string(builtin_unescape(call.Argument(0).string()))
}

View file

@ -1,681 +0,0 @@
package otto
import (
"strconv"
"strings"
)
// Array
func builtinArray(call FunctionCall) Value {
return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
}
func builtinNewArray(self *_object, argumentList []Value) Value {
return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
}
func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
if len(argumentList) == 1 {
firstArgument := argumentList[0]
if firstArgument.IsNumber() {
return runtime.newArray(arrayUint32(runtime, firstArgument))
}
}
return runtime.newArrayOf(argumentList)
}
func builtinArray_toString(call FunctionCall) Value {
thisObject := call.thisObject()
join := thisObject.get("join")
if join.isCallable() {
join := join._object()
return join.call(call.This, call.ArgumentList, false, nativeFrame)
}
return builtinObject_toString(call)
}
func builtinArray_toLocaleString(call FunctionCall) Value {
separator := ","
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
if length == 0 {
return toValue_string("")
}
stringList := make([]string, 0, length)
for index := int64(0); index < length; index += 1 {
value := thisObject.get(arrayIndexToString(index))
stringValue := ""
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
default:
object := call.runtime.toObject(value)
toLocaleString := object.get("toLocaleString")
if !toLocaleString.isCallable() {
panic(call.runtime.panicTypeError())
}
stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
}
stringList = append(stringList, stringValue)
}
return toValue_string(strings.Join(stringList, separator))
}
func builtinArray_concat(call FunctionCall) Value {
thisObject := call.thisObject()
valueArray := []Value{}
source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
for _, item := range source {
switch item.kind {
case valueObject:
object := item._object()
if isArray(object) {
length := object.get("length").number().int64
for index := int64(0); index < length; index += 1 {
name := strconv.FormatInt(index, 10)
if object.hasProperty(name) {
valueArray = append(valueArray, object.get(name))
} else {
valueArray = append(valueArray, Value{})
}
}
continue
}
fallthrough
default:
valueArray = append(valueArray, item)
}
}
return toValue_object(call.runtime.newArrayOf(valueArray))
}
func builtinArray_shift(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
if 0 == length {
thisObject.put("length", toValue_int64(0), true)
return Value{}
}
first := thisObject.get("0")
for index := int64(1); index < length; index++ {
from := arrayIndexToString(index)
to := arrayIndexToString(index - 1)
if thisObject.hasProperty(from) {
thisObject.put(to, thisObject.get(from), true)
} else {
thisObject.delete(to, true)
}
}
thisObject.delete(arrayIndexToString(length-1), true)
thisObject.put("length", toValue_int64(length-1), true)
return first
}
func builtinArray_push(call FunctionCall) Value {
thisObject := call.thisObject()
itemList := call.ArgumentList
index := int64(toUint32(thisObject.get("length")))
for len(itemList) > 0 {
thisObject.put(arrayIndexToString(index), itemList[0], true)
itemList = itemList[1:]
index += 1
}
length := toValue_int64(index)
thisObject.put("length", length, true)
return length
}
func builtinArray_pop(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
if 0 == length {
thisObject.put("length", toValue_uint32(0), true)
return Value{}
}
last := thisObject.get(arrayIndexToString(length - 1))
thisObject.delete(arrayIndexToString(length-1), true)
thisObject.put("length", toValue_int64(length-1), true)
return last
}
func builtinArray_join(call FunctionCall) Value {
separator := ","
{
argument := call.Argument(0)
if argument.IsDefined() {
separator = argument.string()
}
}
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
if length == 0 {
return toValue_string("")
}
stringList := make([]string, 0, length)
for index := int64(0); index < length; index += 1 {
value := thisObject.get(arrayIndexToString(index))
stringValue := ""
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
default:
stringValue = value.string()
}
stringList = append(stringList, stringValue)
}
return toValue_string(strings.Join(stringList, separator))
}
func builtinArray_splice(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
start := valueToRangeIndex(call.Argument(0), length, false)
deleteCount := valueToRangeIndex(call.Argument(1), int64(length)-start, true)
valueArray := make([]Value, deleteCount)
for index := int64(0); index < deleteCount; index++ {
indexString := arrayIndexToString(int64(start + index))
if thisObject.hasProperty(indexString) {
valueArray[index] = thisObject.get(indexString)
}
}
// 0, <1, 2, 3, 4>, 5, 6, 7
// a, b
// length 8 - delete 4 @ start 1
itemList := []Value{}
itemCount := int64(len(call.ArgumentList))
if itemCount > 2 {
itemCount -= 2 // Less the first two arguments
itemList = call.ArgumentList[2:]
} else {
itemCount = 0
}
if itemCount < deleteCount {
// The Object/Array is shrinking
stop := int64(length) - deleteCount
// The new length of the Object/Array before
// appending the itemList remainder
// Stopping at the lower bound of the insertion:
// Move an item from the after the deleted portion
// to a position after the inserted portion
for index := start; index < stop; index++ {
from := arrayIndexToString(index + deleteCount) // Position just after deletion
to := arrayIndexToString(index + itemCount) // Position just after splice (insertion)
if thisObject.hasProperty(from) {
thisObject.put(to, thisObject.get(from), true)
} else {
thisObject.delete(to, true)
}
}
// Delete off the end
// We don't bother to delete below <stop + itemCount> (if any) since those
// will be overwritten anyway
for index := int64(length); index > (stop + itemCount); index-- {
thisObject.delete(arrayIndexToString(index-1), true)
}
} else if itemCount > deleteCount {
// The Object/Array is growing
// The itemCount is greater than the deleteCount, so we do
// not have to worry about overwriting what we should be moving
// ---
// Starting from the upper bound of the deletion:
// Move an item from the after the deleted portion
// to a position after the inserted portion
for index := int64(length) - deleteCount; index > start; index-- {
from := arrayIndexToString(index + deleteCount - 1)
to := arrayIndexToString(index + itemCount - 1)
if thisObject.hasProperty(from) {
thisObject.put(to, thisObject.get(from), true)
} else {
thisObject.delete(to, true)
}
}
}
for index := int64(0); index < itemCount; index++ {
thisObject.put(arrayIndexToString(index+start), itemList[index], true)
}
thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true)
return toValue_object(call.runtime.newArrayOf(valueArray))
}
func builtinArray_slice(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
start, end := rangeStartEnd(call.ArgumentList, length, false)
if start >= end {
// Always an empty array
return toValue_object(call.runtime.newArray(0))
}
sliceLength := end - start
sliceValueArray := make([]Value, sliceLength)
for index := int64(0); index < sliceLength; index++ {
from := arrayIndexToString(index + start)
if thisObject.hasProperty(from) {
sliceValueArray[index] = thisObject.get(from)
}
}
return toValue_object(call.runtime.newArrayOf(sliceValueArray))
}
func builtinArray_unshift(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
itemList := call.ArgumentList
itemCount := int64(len(itemList))
for index := length; index > 0; index-- {
from := arrayIndexToString(index - 1)
to := arrayIndexToString(index + itemCount - 1)
if thisObject.hasProperty(from) {
thisObject.put(to, thisObject.get(from), true)
} else {
thisObject.delete(to, true)
}
}
for index := int64(0); index < itemCount; index++ {
thisObject.put(arrayIndexToString(index), itemList[index], true)
}
newLength := toValue_int64(length + itemCount)
thisObject.put("length", newLength, true)
return newLength
}
func builtinArray_reverse(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
lower := struct {
name string
index int64
exists bool
}{}
upper := lower
lower.index = 0
middle := length / 2 // Division will floor
for lower.index != middle {
lower.name = arrayIndexToString(lower.index)
upper.index = length - lower.index - 1
upper.name = arrayIndexToString(upper.index)
lower.exists = thisObject.hasProperty(lower.name)
upper.exists = thisObject.hasProperty(upper.name)
if lower.exists && upper.exists {
lowerValue := thisObject.get(lower.name)
upperValue := thisObject.get(upper.name)
thisObject.put(lower.name, upperValue, true)
thisObject.put(upper.name, lowerValue, true)
} else if !lower.exists && upper.exists {
value := thisObject.get(upper.name)
thisObject.delete(upper.name, true)
thisObject.put(lower.name, value, true)
} else if lower.exists && !upper.exists {
value := thisObject.get(lower.name)
thisObject.delete(lower.name, true)
thisObject.put(upper.name, value, true)
} else {
// Nothing happens.
}
lower.index += 1
}
return call.This
}
func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int {
j := struct {
name string
exists bool
defined bool
value string
}{}
k := j
j.name = arrayIndexToString(int64(index0))
j.exists = thisObject.hasProperty(j.name)
k.name = arrayIndexToString(int64(index1))
k.exists = thisObject.hasProperty(k.name)
if !j.exists && !k.exists {
return 0
} else if !j.exists {
return 1
} else if !k.exists {
return -1
}
x := thisObject.get(j.name)
y := thisObject.get(k.name)
j.defined = x.IsDefined()
k.defined = y.IsDefined()
if !j.defined && !k.defined {
return 0
} else if !j.defined {
return 1
} else if !k.defined {
return -1
}
if compare == nil {
j.value = x.string()
k.value = y.string()
if j.value == k.value {
return 0
} else if j.value < k.value {
return -1
}
return 1
}
return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame)))
}
func arraySortSwap(thisObject *_object, index0, index1 uint) {
j := struct {
name string
exists bool
}{}
k := j
j.name = arrayIndexToString(int64(index0))
j.exists = thisObject.hasProperty(j.name)
k.name = arrayIndexToString(int64(index1))
k.exists = thisObject.hasProperty(k.name)
if j.exists && k.exists {
jValue := thisObject.get(j.name)
kValue := thisObject.get(k.name)
thisObject.put(j.name, kValue, true)
thisObject.put(k.name, jValue, true)
} else if !j.exists && k.exists {
value := thisObject.get(k.name)
thisObject.delete(k.name, true)
thisObject.put(j.name, value, true)
} else if j.exists && !k.exists {
value := thisObject.get(j.name)
thisObject.delete(j.name, true)
thisObject.put(k.name, value, true)
} else {
// Nothing happens.
}
}
func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) {
arraySortSwap(thisObject, pivot, right) // Right is now the pivot value
cursor := left
cursor2 := left
for index := left; index < right; index++ {
comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value
if comparison < 0 {
arraySortSwap(thisObject, index, cursor)
if cursor < cursor2 {
arraySortSwap(thisObject, index, cursor2)
}
cursor += 1
cursor2 += 1
} else if comparison == 0 {
arraySortSwap(thisObject, index, cursor2)
cursor2 += 1
}
}
arraySortSwap(thisObject, cursor2, right)
return cursor, cursor2
}
func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) {
if left < right {
middle := left + (right-left)/2
pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare)
if pivot > 0 {
arraySortQuickSort(thisObject, left, pivot-1, compare)
}
arraySortQuickSort(thisObject, pivot2+1, right, compare)
}
}
func builtinArray_sort(call FunctionCall) Value {
thisObject := call.thisObject()
length := uint(toUint32(thisObject.get("length")))
compareValue := call.Argument(0)
compare := compareValue._object()
if compareValue.IsUndefined() {
} else if !compareValue.isCallable() {
panic(call.runtime.panicTypeError())
}
if length > 1 {
arraySortQuickSort(thisObject, 0, length-1, compare)
}
return call.This
}
func builtinArray_isArray(call FunctionCall) Value {
return toValue_bool(isArray(call.Argument(0)._object()))
}
func builtinArray_indexOf(call FunctionCall) Value {
thisObject, matchValue := call.thisObject(), call.Argument(0)
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
index := int64(0)
if len(call.ArgumentList) > 1 {
index = call.Argument(1).number().int64
}
if index < 0 {
if index += length; index < 0 {
index = 0
}
} else if index >= length {
index = -1
}
for ; index >= 0 && index < length; index++ {
name := arrayIndexToString(int64(index))
if !thisObject.hasProperty(name) {
continue
}
value := thisObject.get(name)
if strictEqualityComparison(matchValue, value) {
return toValue_uint32(uint32(index))
}
}
}
return toValue_int(-1)
}
func builtinArray_lastIndexOf(call FunctionCall) Value {
thisObject, matchValue := call.thisObject(), call.Argument(0)
length := int64(toUint32(thisObject.get("length")))
index := length - 1
if len(call.ArgumentList) > 1 {
index = call.Argument(1).number().int64
}
if 0 > index {
index += length
}
if index > length {
index = length - 1
} else if 0 > index {
return toValue_int(-1)
}
for ; index >= 0; index-- {
name := arrayIndexToString(int64(index))
if !thisObject.hasProperty(name) {
continue
}
value := thisObject.get(name)
if strictEqualityComparison(matchValue, value) {
return toValue_uint32(uint32(index))
}
}
return toValue_int(-1)
}
func builtinArray_every(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
continue
}
return falseValue
}
}
return trueValue
}
panic(call.runtime.panicTypeError())
}
func builtinArray_some(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
return trueValue
}
}
}
return falseValue
}
panic(call.runtime.panicTypeError())
}
func builtinArray_forEach(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
}
}
return Value{}
}
panic(call.runtime.panicTypeError())
}
func builtinArray_map(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
callThis := call.Argument(1)
values := make([]Value, length)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
} else {
values[index] = Value{}
}
}
return toValue_object(call.runtime.newArrayOf(values))
}
panic(call.runtime.panicTypeError())
}
func builtinArray_filter(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
callThis := call.Argument(1)
values := make([]Value, 0)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
value := thisObject.get(key)
if iterator.call(call.runtime, callThis, value, index, this).bool() {
values = append(values, value)
}
}
}
return toValue_object(call.runtime.newArrayOf(values))
}
panic(call.runtime.panicTypeError())
}
func builtinArray_reduce(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
initial := len(call.ArgumentList) > 1
start := call.Argument(1)
length := int64(toUint32(thisObject.get("length")))
index := int64(0)
if length > 0 || initial {
var accumulator Value
if !initial {
for ; index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = thisObject.get(key)
index++
break
}
}
} else {
accumulator = start
}
for ; index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
}
}
return accumulator
}
}
panic(call.runtime.panicTypeError())
}
func builtinArray_reduceRight(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
initial := len(call.ArgumentList) > 1
start := call.Argument(1)
length := int64(toUint32(thisObject.get("length")))
if length > 0 || initial {
index := length - 1
var accumulator Value
if !initial {
for ; index >= 0; index-- {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = thisObject.get(key)
index--
break
}
}
} else {
accumulator = start
}
for ; index >= 0; index-- {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
}
}
return accumulator
}
}
panic(call.runtime.panicTypeError())
}

View file

@ -1,28 +0,0 @@
package otto
// Boolean
func builtinBoolean(call FunctionCall) Value {
return toValue_bool(call.Argument(0).bool())
}
func builtinNewBoolean(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0)))
}
func builtinBoolean_toString(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
// Will throw a TypeError if ThisObject is not a Boolean
value = call.thisClassObject("Boolean").primitiveValue()
}
return toValue_string(value.string())
}
func builtinBoolean_valueOf(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
value = call.thisClassObject("Boolean").primitiveValue()
}
return value
}

View file

@ -1,615 +0,0 @@
package otto
import (
"math"
Time "time"
)
// Date
const (
// TODO Be like V8?
// builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)"
builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
builtinDate_goDateLayout = "Mon, 02 Jan 2006"
builtinDate_goTimeLayout = "15:04:05 MST"
)
func builtinDate(call FunctionCall) Value {
date := &_dateObject{}
date.Set(newDateTime([]Value{}, Time.Local))
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
}
func builtinNewDate(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
}
func builtinDate_toString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout))
}
func builtinDate_toDateString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout))
}
func builtinDate_toTimeString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout))
}
func builtinDate_toUTCString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
}
func builtinDate_toISOString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z"))
}
func builtinDate_toJSON(call FunctionCall) Value {
object := call.thisObject()
value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
{ // FIXME value.isFinite
value := value.float64()
if math.IsNaN(value) || math.IsInf(value, 0) {
return nullValue
}
}
toISOString := object.get("toISOString")
if !toISOString.isCallable() {
// FIXME
panic(call.runtime.panicTypeError())
}
return toISOString.call(call.runtime, toValue_object(object), []Value{})
}
func builtinDate_toGMTString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
}
func builtinDate_getTime(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
// We do this (convert away from a float) so the user
// does not get something back in exponential notation
return toValue_int64(int64(date.Epoch()))
}
func builtinDate_setTime(call FunctionCall) Value {
object := call.thisObject()
date := dateObjectOf(call.runtime, call.thisObject())
date.Set(call.Argument(0).float64())
object.value = date
return date.Value()
}
func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
object := call.thisObject()
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return nil, nil, nil, nil
}
if argumentLimit > len(call.ArgumentList) {
argumentLimit = len(call.ArgumentList)
}
if argumentLimit == 0 {
object.value = invalidDateObject
return nil, nil, nil, nil
}
valueList := make([]int, argumentLimit)
for index := 0; index < argumentLimit; index++ {
value := call.ArgumentList[index]
nm := value.number()
switch nm.kind {
case numberInteger, numberFloat:
default:
object.value = invalidDateObject
return nil, nil, nil, nil
}
valueList[index] = int(nm.int64)
}
baseTime := date.Time()
if timeLocal {
baseTime = baseTime.Local()
}
ecmaTime := ecmaTime(baseTime)
return object, &date, &ecmaTime, valueList
}
func builtinDate_parse(call FunctionCall) Value {
date := call.Argument(0).string()
return toValue_float64(dateParse(date))
}
func builtinDate_UTC(call FunctionCall) Value {
return toValue_float64(newDateTime(call.ArgumentList, Time.UTC))
}
func builtinDate_now(call FunctionCall) Value {
call.ArgumentList = []Value(nil)
return builtinDate_UTC(call)
}
// This is a placeholder
func builtinDate_toLocaleString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05"))
}
// This is a placeholder
func builtinDate_toLocaleDateString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format("2006-01-02"))
}
// This is a placeholder
func builtinDate_toLocaleTimeString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return toValue_string("Invalid Date")
}
return toValue_string(date.Time().Local().Format("15:04:05"))
}
func builtinDate_valueOf(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return date.Value()
}
func builtinDate_getYear(call FunctionCall) Value {
// Will throw a TypeError is ThisObject is nil or
// does not have Class of "Date"
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Year() - 1900)
}
func builtinDate_getFullYear(call FunctionCall) Value {
// Will throw a TypeError is ThisObject is nil or
// does not have Class of "Date"
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Year())
}
func builtinDate_getUTCFullYear(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Year())
}
func builtinDate_getMonth(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(dateFromGoMonth(date.Time().Local().Month()))
}
func builtinDate_getUTCMonth(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(dateFromGoMonth(date.Time().Month()))
}
func builtinDate_getDate(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Day())
}
func builtinDate_getUTCDate(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Day())
}
func builtinDate_getDay(call FunctionCall) Value {
// Actually day of the week
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(dateFromGoDay(date.Time().Local().Weekday()))
}
func builtinDate_getUTCDay(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(dateFromGoDay(date.Time().Weekday()))
}
func builtinDate_getHours(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Hour())
}
func builtinDate_getUTCHours(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Hour())
}
func builtinDate_getMinutes(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Minute())
}
func builtinDate_getUTCMinutes(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Minute())
}
func builtinDate_getSeconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Second())
}
func builtinDate_getUTCSeconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Second())
}
func builtinDate_getMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100))
}
func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100))
}
func builtinDate_getTimezoneOffset(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
timeLocal := date.Time().Local()
// Is this kosher?
timeLocalAsUTC := Time.Date(
timeLocal.Year(),
timeLocal.Month(),
timeLocal.Day(),
timeLocal.Hour(),
timeLocal.Minute(),
timeLocal.Second(),
timeLocal.Nanosecond(),
Time.UTC,
)
return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
}
func builtinDate_setMilliseconds(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.millisecond = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCMilliseconds(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.millisecond = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setSeconds(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.millisecond = value[1]
}
ecmaTime.second = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCSeconds(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.millisecond = value[1]
}
ecmaTime.second = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setMinutes(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.millisecond = value[2]
ecmaTime.second = value[1]
} else if len(value) > 1 {
ecmaTime.second = value[1]
}
ecmaTime.minute = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCMinutes(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.millisecond = value[2]
ecmaTime.second = value[1]
} else if len(value) > 1 {
ecmaTime.second = value[1]
}
ecmaTime.minute = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setHours(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 3 {
ecmaTime.millisecond = value[3]
ecmaTime.second = value[2]
ecmaTime.minute = value[1]
} else if len(value) > 2 {
ecmaTime.second = value[2]
ecmaTime.minute = value[1]
} else if len(value) > 1 {
ecmaTime.minute = value[1]
}
ecmaTime.hour = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCHours(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 3 {
ecmaTime.millisecond = value[3]
ecmaTime.second = value[2]
ecmaTime.minute = value[1]
} else if len(value) > 2 {
ecmaTime.second = value[2]
ecmaTime.minute = value[1]
} else if len(value) > 1 {
ecmaTime.minute = value[1]
}
ecmaTime.hour = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setDate(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.day = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCDate(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.day = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setMonth(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.day = value[1]
}
ecmaTime.month = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCMonth(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.day = value[1]
}
ecmaTime.month = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setYear(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
year := value[0]
if 0 <= year && year <= 99 {
year += 1900
}
ecmaTime.year = year
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setFullYear(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.day = value[2]
ecmaTime.month = value[1]
} else if len(value) > 1 {
ecmaTime.month = value[1]
}
ecmaTime.year = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
func builtinDate_setUTCFullYear(call FunctionCall) Value {
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.day = value[2]
ecmaTime.month = value[1]
} else if len(value) > 1 {
ecmaTime.month = value[1]
}
ecmaTime.year = value[0]
date.SetTime(ecmaTime.goTime())
object.value = *date
return date.Value()
}
// toUTCString
// toISOString
// toJSONString
// toJSON

View file

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

View file

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

View file

@ -1,299 +0,0 @@
package otto
import (
"bytes"
"encoding/json"
"fmt"
"strings"
)
type _builtinJSON_parseContext struct {
call FunctionCall
reviver Value
}
func builtinJSON_parse(call FunctionCall) Value {
ctx := _builtinJSON_parseContext{
call: call,
}
revive := false
if reviver := call.Argument(1); reviver.isCallable() {
revive = true
ctx.reviver = reviver
}
var root interface{}
err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
if err != nil {
panic(call.runtime.panicSyntaxError(err.Error()))
}
value, exists := builtinJSON_parseWalk(ctx, root)
if !exists {
value = Value{}
}
if revive {
root := ctx.call.runtime.newObject()
root.put("", value, false)
return builtinJSON_reviveWalk(ctx, root, "")
}
return value
}
func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value {
value := holder.get(name)
if object := value._object(); object != nil {
if isArray(object) {
length := int64(objectLength(object))
for index := int64(0); index < length; index += 1 {
name := arrayIndexToString(index)
value := builtinJSON_reviveWalk(ctx, object, name)
if value.IsUndefined() {
object.delete(name, false)
} else {
object.defineProperty(name, value, 0111, false)
}
}
} else {
object.enumerate(false, func(name string) bool {
value := builtinJSON_reviveWalk(ctx, object, name)
if value.IsUndefined() {
object.delete(name, false)
} else {
object.defineProperty(name, value, 0111, false)
}
return true
})
}
}
return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
}
func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
switch value := rawValue.(type) {
case nil:
return nullValue, true
case bool:
return toValue_bool(value), true
case string:
return toValue_string(value), true
case float64:
return toValue_float64(value), true
case []interface{}:
arrayValue := make([]Value, len(value))
for index, rawValue := range value {
if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
arrayValue[index] = value
}
}
return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true
case map[string]interface{}:
object := ctx.call.runtime.newObject()
for name, rawValue := range value {
if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
object.put(name, value, false)
}
}
return toValue_object(object), true
}
return Value{}, false
}
type _builtinJSON_stringifyContext struct {
call FunctionCall
stack []*_object
propertyList []string
replacerFunction *Value
gap string
}
func builtinJSON_stringify(call FunctionCall) Value {
ctx := _builtinJSON_stringifyContext{
call: call,
stack: []*_object{nil},
}
replacer := call.Argument(1)._object()
if replacer != nil {
if isArray(replacer) {
length := objectLength(replacer)
seen := map[string]bool{}
propertyList := make([]string, length)
length = 0
for index, _ := range propertyList {
value := replacer.get(arrayIndexToString(int64(index)))
switch value.kind {
case valueObject:
switch value.value.(*_object).class {
case "String":
case "Number":
default:
continue
}
case valueString:
case valueNumber:
default:
continue
}
name := value.string()
if seen[name] {
continue
}
seen[name] = true
length += 1
propertyList[index] = name
}
ctx.propertyList = propertyList[0:length]
} else if replacer.class == "Function" {
value := toValue_object(replacer)
ctx.replacerFunction = &value
}
}
if spaceValue, exists := call.getArgument(2); exists {
if spaceValue.kind == valueObject {
switch spaceValue.value.(*_object).class {
case "String":
spaceValue = toValue_string(spaceValue.string())
case "Number":
spaceValue = spaceValue.numberValue()
}
}
switch spaceValue.kind {
case valueString:
value := spaceValue.string()
if len(value) > 10 {
ctx.gap = value[0:10]
} else {
ctx.gap = value
}
case valueNumber:
value := spaceValue.number().int64
if value > 10 {
value = 10
} else if value < 0 {
value = 0
}
ctx.gap = strings.Repeat(" ", int(value))
}
}
holder := call.runtime.newObject()
holder.put("", call.Argument(0), false)
value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
if !exists {
return Value{}
}
valueJSON, err := json.Marshal(value)
if err != nil {
panic(call.runtime.panicTypeError(err.Error()))
}
if ctx.gap != "" {
valueJSON1 := bytes.Buffer{}
json.Indent(&valueJSON1, valueJSON, "", ctx.gap)
valueJSON = valueJSON1.Bytes()
}
return toValue_string(string(valueJSON))
}
func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) {
value := holder.get(key)
if value.IsObject() {
object := value._object()
if toJSON := object.get("toJSON"); toJSON.IsFunction() {
value = toJSON.call(ctx.call.runtime, value, key)
} else {
// If the object is a GoStruct or something that implements json.Marshaler
if object.objectClass.marshalJSON != nil {
marshaler := object.objectClass.marshalJSON(object)
if marshaler != nil {
return marshaler, true
}
}
}
}
if ctx.replacerFunction != nil {
value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
}
if value.kind == valueObject {
switch value.value.(*_object).class {
case "Boolean":
value = value._object().value.(Value)
case "String":
value = toValue_string(value.string())
case "Number":
value = value.numberValue()
}
}
switch value.kind {
case valueBoolean:
return value.bool(), true
case valueString:
return value.string(), true
case valueNumber:
integer := value.number()
switch integer.kind {
case numberInteger:
return integer.int64, true
case numberFloat:
return integer.float64, true
default:
return nil, true
}
case valueNull:
return nil, true
case valueObject:
holder := value._object()
if value := value._object(); nil != value {
for _, object := range ctx.stack {
if holder == object {
panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
}
}
ctx.stack = append(ctx.stack, value)
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
}
if isArray(holder) {
var length uint32
switch value := holder.get("length").value.(type) {
case uint32:
length = value
case int:
if value >= 0 {
length = uint32(value)
}
default:
panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
}
array := make([]interface{}, length)
for index, _ := range array {
name := arrayIndexToString(int64(index))
value, _ := builtinJSON_stringifyWalk(ctx, name, holder)
array[index] = value
}
return array, true
} else if holder.class != "Function" {
object := map[string]interface{}{}
if ctx.propertyList != nil {
for _, name := range ctx.propertyList {
value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
if exists {
object[name] = value
}
}
} else {
// Go maps are without order, so this doesn't conform to the ECMA ordering
// standard, but oh well...
holder.enumerate(false, func(name string) bool {
value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
if exists {
object[name] = value
}
return true
})
}
return object, true
}
}
return nil, false
}

View file

@ -1,151 +0,0 @@
package otto
import (
"math"
"math/rand"
)
// Math
func builtinMath_abs(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Abs(number))
}
func builtinMath_acos(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Acos(number))
}
func builtinMath_asin(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Asin(number))
}
func builtinMath_atan(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Atan(number))
}
func builtinMath_atan2(call FunctionCall) Value {
y := call.Argument(0).float64()
if math.IsNaN(y) {
return NaNValue()
}
x := call.Argument(1).float64()
if math.IsNaN(x) {
return NaNValue()
}
return toValue_float64(math.Atan2(y, x))
}
func builtinMath_cos(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Cos(number))
}
func builtinMath_ceil(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Ceil(number))
}
func builtinMath_exp(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Exp(number))
}
func builtinMath_floor(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Floor(number))
}
func builtinMath_log(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Log(number))
}
func builtinMath_max(call FunctionCall) Value {
switch len(call.ArgumentList) {
case 0:
return negativeInfinityValue()
case 1:
return toValue_float64(call.ArgumentList[0].float64())
}
result := call.ArgumentList[0].float64()
if math.IsNaN(result) {
return NaNValue()
}
for _, value := range call.ArgumentList[1:] {
value := value.float64()
if math.IsNaN(value) {
return NaNValue()
}
result = math.Max(result, value)
}
return toValue_float64(result)
}
func builtinMath_min(call FunctionCall) Value {
switch len(call.ArgumentList) {
case 0:
return positiveInfinityValue()
case 1:
return toValue_float64(call.ArgumentList[0].float64())
}
result := call.ArgumentList[0].float64()
if math.IsNaN(result) {
return NaNValue()
}
for _, value := range call.ArgumentList[1:] {
value := value.float64()
if math.IsNaN(value) {
return NaNValue()
}
result = math.Min(result, value)
}
return toValue_float64(result)
}
func builtinMath_pow(call FunctionCall) Value {
// TODO Make sure this works according to the specification (15.8.2.13)
x := call.Argument(0).float64()
y := call.Argument(1).float64()
if math.Abs(x) == 1 && math.IsInf(y, 0) {
return NaNValue()
}
return toValue_float64(math.Pow(x, y))
}
func builtinMath_random(call FunctionCall) Value {
var v float64
if call.runtime.random != nil {
v = call.runtime.random()
} else {
v = rand.Float64()
}
return toValue_float64(v)
}
func builtinMath_round(call FunctionCall) Value {
number := call.Argument(0).float64()
value := math.Floor(number + 0.5)
if value == 0 {
value = math.Copysign(0, number)
}
return toValue_float64(value)
}
func builtinMath_sin(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Sin(number))
}
func builtinMath_sqrt(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Sqrt(number))
}
func builtinMath_tan(call FunctionCall) Value {
number := call.Argument(0).float64()
return toValue_float64(math.Tan(number))
}

View file

@ -1,93 +0,0 @@
package otto
import (
"math"
"strconv"
)
// Number
func numberValueFromNumberArgumentList(argumentList []Value) Value {
if len(argumentList) > 0 {
return argumentList[0].numberValue()
}
return toValue_int(0)
}
func builtinNumber(call FunctionCall) Value {
return numberValueFromNumberArgumentList(call.ArgumentList)
}
func builtinNewNumber(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList)))
}
func builtinNumber_toString(call FunctionCall) Value {
// Will throw a TypeError if ThisObject is not a Number
value := call.thisClassObject("Number").primitiveValue()
radix := 10
radixArgument := call.Argument(0)
if radixArgument.IsDefined() {
integer := toIntegerFloat(radixArgument)
if integer < 2 || integer > 36 {
panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36"))
}
radix = int(integer)
}
if radix == 10 {
return toValue_string(value.string())
}
return toValue_string(numberToStringRadix(value, radix))
}
func builtinNumber_valueOf(call FunctionCall) Value {
return call.thisClassObject("Number").primitiveValue()
}
func builtinNumber_toFixed(call FunctionCall) Value {
precision := toIntegerFloat(call.Argument(0))
if 20 < precision || 0 > precision {
panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20"))
}
if call.This.IsNaN() {
return toValue_string("NaN")
}
value := call.This.float64()
if math.Abs(value) >= 1e21 {
return toValue_string(floatToString(value, 64))
}
return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64))
}
func builtinNumber_toExponential(call FunctionCall) Value {
if call.This.IsNaN() {
return toValue_string("NaN")
}
precision := float64(-1)
if value := call.Argument(0); value.IsDefined() {
precision = toIntegerFloat(value)
if 0 > precision {
panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36"))
}
}
return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64))
}
func builtinNumber_toPrecision(call FunctionCall) Value {
if call.This.IsNaN() {
return toValue_string("NaN")
}
value := call.Argument(0)
if value.IsUndefined() {
return toValue_string(call.This.string())
}
precision := toIntegerFloat(value)
if 1 > precision {
panic(call.runtime.panicRangeError("toPrecision() precision must be greater than 1"))
}
return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64))
}
func builtinNumber_toLocaleString(call FunctionCall) Value {
return builtinNumber_toString(call)
}

View file

@ -1,289 +0,0 @@
package otto
import (
"fmt"
)
// Object
func builtinObject(call FunctionCall) Value {
value := call.Argument(0)
switch value.kind {
case valueUndefined, valueNull:
return toValue_object(call.runtime.newObject())
}
return toValue_object(call.runtime.toObject(value))
}
func builtinNewObject(self *_object, argumentList []Value) Value {
value := valueOfArrayIndex(argumentList, 0)
switch value.kind {
case valueNull, valueUndefined:
case valueNumber, valueString, valueBoolean:
return toValue_object(self.runtime.toObject(value))
case valueObject:
return value
default:
}
return toValue_object(self.runtime.newObject())
}
func builtinObject_valueOf(call FunctionCall) Value {
return toValue_object(call.thisObject())
}
func builtinObject_hasOwnProperty(call FunctionCall) Value {
propertyName := call.Argument(0).string()
thisObject := call.thisObject()
return toValue_bool(thisObject.hasOwnProperty(propertyName))
}
func builtinObject_isPrototypeOf(call FunctionCall) Value {
value := call.Argument(0)
if !value.IsObject() {
return falseValue
}
prototype := call.toObject(value).prototype
thisObject := call.thisObject()
for prototype != nil {
if thisObject == prototype {
return trueValue
}
prototype = prototype.prototype
}
return falseValue
}
func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
propertyName := call.Argument(0).string()
thisObject := call.thisObject()
property := thisObject.getOwnProperty(propertyName)
if property != nil && property.enumerable() {
return trueValue
}
return falseValue
}
func builtinObject_toString(call FunctionCall) Value {
result := ""
if call.This.IsUndefined() {
result = "[object Undefined]"
} else if call.This.IsNull() {
result = "[object Null]"
} else {
result = fmt.Sprintf("[object %s]", call.thisObject().class)
}
return toValue_string(result)
}
func builtinObject_toLocaleString(call FunctionCall) Value {
toString := call.thisObject().get("toString")
if !toString.isCallable() {
panic(call.runtime.panicTypeError())
}
return toString.call(call.runtime, call.This)
}
func builtinObject_getPrototypeOf(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(call.runtime.panicTypeError())
}
if object.prototype == nil {
return nullValue
}
return toValue_object(object.prototype)
}
func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(call.runtime.panicTypeError())
}
name := call.Argument(1).string()
descriptor := object.getOwnProperty(name)
if descriptor == nil {
return Value{}
}
return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor))
}
func builtinObject_defineProperty(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(call.runtime.panicTypeError())
}
name := call.Argument(1).string()
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
object.defineOwnProperty(name, descriptor, true)
return objectValue
}
func builtinObject_defineProperties(call FunctionCall) Value {
objectValue := call.Argument(0)
object := objectValue._object()
if object == nil {
panic(call.runtime.panicTypeError())
}
properties := call.runtime.toObject(call.Argument(1))
properties.enumerate(false, func(name string) bool {
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
object.defineOwnProperty(name, descriptor, true)
return true
})
return objectValue
}
func builtinObject_create(call FunctionCall) Value {
prototypeValue := call.Argument(0)
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
panic(call.runtime.panicTypeError())
}
object := call.runtime.newObject()
object.prototype = prototypeValue._object()
propertiesValue := call.Argument(1)
if propertiesValue.IsDefined() {
properties := call.runtime.toObject(propertiesValue)
properties.enumerate(false, func(name string) bool {
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
object.defineOwnProperty(name, descriptor, true)
return true
})
}
return toValue_object(object)
}
func builtinObject_isExtensible(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
return toValue_bool(object.extensible)
}
panic(call.runtime.panicTypeError())
}
func builtinObject_preventExtensions(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
object.extensible = false
} else {
panic(call.runtime.panicTypeError())
}
return object
}
func builtinObject_isSealed(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
if object.extensible {
return toValue_bool(false)
}
result := true
object.enumerate(true, func(name string) bool {
property := object.getProperty(name)
if property.configurable() {
result = false
}
return true
})
return toValue_bool(result)
}
panic(call.runtime.panicTypeError())
}
func builtinObject_seal(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
object.enumerate(true, func(name string) bool {
if property := object.getOwnProperty(name); nil != property && property.configurable() {
property.configureOff()
object.defineOwnProperty(name, *property, true)
}
return true
})
object.extensible = false
} else {
panic(call.runtime.panicTypeError())
}
return object
}
func builtinObject_isFrozen(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
if object.extensible {
return toValue_bool(false)
}
result := true
object.enumerate(true, func(name string) bool {
property := object.getProperty(name)
if property.configurable() || property.writable() {
result = false
}
return true
})
return toValue_bool(result)
}
panic(call.runtime.panicTypeError())
}
func builtinObject_freeze(call FunctionCall) Value {
object := call.Argument(0)
if object := object._object(); object != nil {
object.enumerate(true, func(name string) bool {
if property, update := object.getOwnProperty(name), false; nil != property {
if property.isDataDescriptor() && property.writable() {
property.writeOff()
update = true
}
if property.configurable() {
property.configureOff()
update = true
}
if update {
object.defineOwnProperty(name, *property, true)
}
}
return true
})
object.extensible = false
} else {
panic(call.runtime.panicTypeError())
}
return object
}
func builtinObject_keys(call FunctionCall) Value {
if object, keys := call.Argument(0)._object(), []Value(nil); nil != object {
object.enumerate(false, func(name string) bool {
keys = append(keys, toValue_string(name))
return true
})
return toValue_object(call.runtime.newArrayOf(keys))
}
panic(call.runtime.panicTypeError())
}
func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
if object, propertyNames := call.Argument(0)._object(), []Value(nil); nil != object {
object.enumerate(true, func(name string) bool {
if object.hasOwnProperty(name) {
propertyNames = append(propertyNames, toValue_string(name))
}
return true
})
return toValue_object(call.runtime.newArrayOf(propertyNames))
}
panic(call.runtime.panicTypeError())
}

View file

@ -1,65 +0,0 @@
package otto
import (
"fmt"
)
// RegExp
func builtinRegExp(call FunctionCall) Value {
pattern := call.Argument(0)
flags := call.Argument(1)
if object := pattern._object(); object != nil {
if object.class == "RegExp" && flags.IsUndefined() {
return pattern
}
}
return toValue_object(call.runtime.newRegExp(pattern, flags))
}
func builtinNewRegExp(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newRegExp(
valueOfArrayIndex(argumentList, 0),
valueOfArrayIndex(argumentList, 1),
))
}
func builtinRegExp_toString(call FunctionCall) Value {
thisObject := call.thisObject()
source := thisObject.get("source").string()
flags := []byte{}
if thisObject.get("global").bool() {
flags = append(flags, 'g')
}
if thisObject.get("ignoreCase").bool() {
flags = append(flags, 'i')
}
if thisObject.get("multiline").bool() {
flags = append(flags, 'm')
}
return toValue_string(fmt.Sprintf("/%s/%s", source, flags))
}
func builtinRegExp_exec(call FunctionCall) Value {
thisObject := call.thisObject()
target := call.Argument(0).string()
match, result := execRegExp(thisObject, target)
if !match {
return nullValue
}
return toValue_object(execResultToArray(call.runtime, target, result))
}
func builtinRegExp_test(call FunctionCall) Value {
thisObject := call.thisObject()
target := call.Argument(0).string()
match, _ := execRegExp(thisObject, target)
return toValue_bool(match)
}
func builtinRegExp_compile(call FunctionCall) Value {
// This (useless) function is deprecated, but is here to provide some
// semblance of compatibility.
// Caveat emptor: it may not be around for long.
return Value{}
}

View file

@ -1,500 +0,0 @@
package otto
import (
"bytes"
"regexp"
"strconv"
"strings"
"unicode/utf8"
)
// String
func stringValueFromStringArgumentList(argumentList []Value) Value {
if len(argumentList) > 0 {
return toValue_string(argumentList[0].string())
}
return toValue_string("")
}
func builtinString(call FunctionCall) Value {
return stringValueFromStringArgumentList(call.ArgumentList)
}
func builtinNewString(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
}
func builtinString_toString(call FunctionCall) Value {
return call.thisClassObject("String").primitiveValue()
}
func builtinString_valueOf(call FunctionCall) Value {
return call.thisClassObject("String").primitiveValue()
}
func builtinString_fromCharCode(call FunctionCall) Value {
chrList := make([]uint16, len(call.ArgumentList))
for index, value := range call.ArgumentList {
chrList[index] = toUint16(value)
}
return toValue_string16(chrList)
}
func builtinString_charAt(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
idx := int(call.Argument(0).number().int64)
chr := stringAt(call.This._object().stringValue(), idx)
if chr == utf8.RuneError {
return toValue_string("")
}
return toValue_string(string(chr))
}
func builtinString_charCodeAt(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
idx := int(call.Argument(0).number().int64)
chr := stringAt(call.This._object().stringValue(), idx)
if chr == utf8.RuneError {
return NaNValue()
}
return toValue_uint16(uint16(chr))
}
func builtinString_concat(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
var value bytes.Buffer
value.WriteString(call.This.string())
for _, item := range call.ArgumentList {
value.WriteString(item.string())
}
return toValue_string(value.String())
}
func builtinString_indexOf(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
value := call.This.string()
target := call.Argument(0).string()
if 2 > len(call.ArgumentList) {
return toValue_int(strings.Index(value, target))
}
start := toIntegerFloat(call.Argument(1))
if 0 > start {
start = 0
} else if start >= float64(len(value)) {
if target == "" {
return toValue_int(len(value))
}
return toValue_int(-1)
}
index := strings.Index(value[int(start):], target)
if index >= 0 {
index += int(start)
}
return toValue_int(index)
}
func builtinString_lastIndexOf(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
value := call.This.string()
target := call.Argument(0).string()
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
return toValue_int(strings.LastIndex(value, target))
}
length := len(value)
if length == 0 {
return toValue_int(strings.LastIndex(value, target))
}
start := call.ArgumentList[1].number()
if start.kind == numberInfinity { // FIXME
// startNumber is infinity, so start is the end of string (start = length)
return toValue_int(strings.LastIndex(value, target))
}
if 0 > start.int64 {
start.int64 = 0
}
end := int(start.int64) + len(target)
if end > length {
end = length
}
return toValue_int(strings.LastIndex(value[:end], target))
}
func builtinString_match(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
matcherValue := call.Argument(0)
matcher := matcherValue._object()
if !matcherValue.IsObject() || matcher.class != "RegExp" {
matcher = call.runtime.newRegExp(matcherValue, Value{})
}
global := matcher.get("global").bool()
if !global {
match, result := execRegExp(matcher, target)
if !match {
return nullValue
}
return toValue_object(execResultToArray(call.runtime, target, result))
}
{
result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1)
matchCount := len(result)
if result == nil {
matcher.put("lastIndex", toValue_int(0), true)
return Value{} // !match
}
matchCount = len(result)
valueArray := make([]Value, matchCount)
for index := 0; index < matchCount; index++ {
valueArray[index] = toValue_string(target[result[index][0]:result[index][1]])
}
matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true)
return toValue_object(call.runtime.newArrayOf(valueArray))
}
}
var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])")
func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) {
matchCount := len(match) / 2
output = input
if match[0] != lastIndex {
output = append(output, target[lastIndex:match[0]]...)
}
replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte {
// TODO Check if match[0] or match[1] can be -1 in this scenario
switch part[1] {
case '$':
return []byte{'$'}
case '&':
return target[match[0]:match[1]]
case '`':
return target[:match[0]]
case '\'':
return target[match[1]:len(target)]
}
matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64)
matchNumber := int(matchNumberParse)
if error != nil || matchNumber >= matchCount {
return []byte{}
}
offset := 2 * matchNumber
if match[offset] != -1 {
return target[match[offset]:match[offset+1]]
}
return []byte{} // The empty string
})
output = append(output, replacement...)
return output
}
func builtinString_replace(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := []byte(call.This.string())
searchValue := call.Argument(0)
searchObject := searchValue._object()
// TODO If a capture is -1?
var search *regexp.Regexp
global := false
find := 1
if searchValue.IsObject() && searchObject.class == "RegExp" {
regExp := searchObject.regExpValue()
search = regExp.regularExpression
if regExp.global {
find = -1
}
} else {
search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string()))
}
found := search.FindAllSubmatchIndex(target, find)
if found == nil {
return toValue_string(string(target)) // !match
}
{
lastIndex := 0
result := []byte{}
replaceValue := call.Argument(1)
if replaceValue.isCallable() {
target := string(target)
replace := replaceValue._object()
for _, match := range found {
if match[0] != lastIndex {
result = append(result, target[lastIndex:match[0]]...)
}
matchCount := len(match) / 2
argumentList := make([]Value, matchCount+2)
for index := 0; index < matchCount; index++ {
offset := 2 * index
if match[offset] != -1 {
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
} else {
argumentList[index] = Value{}
}
}
argumentList[matchCount+0] = toValue_int(match[0])
argumentList[matchCount+1] = toValue_string(target)
replacement := replace.call(Value{}, argumentList, false, nativeFrame).string()
result = append(result, []byte(replacement)...)
lastIndex = match[1]
}
} else {
replace := []byte(replaceValue.string())
for _, match := range found {
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
lastIndex = match[1]
}
}
if lastIndex != len(target) {
result = append(result, target[lastIndex:]...)
}
if global && searchObject != nil {
searchObject.put("lastIndex", toValue_int(lastIndex), true)
}
return toValue_string(string(result))
}
}
func builtinString_search(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
searchValue := call.Argument(0)
search := searchValue._object()
if !searchValue.IsObject() || search.class != "RegExp" {
search = call.runtime.newRegExp(searchValue, Value{})
}
result := search.regExpValue().regularExpression.FindStringIndex(target)
if result == nil {
return toValue_int(-1)
}
return toValue_int(result[0])
}
func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) {
if int64(index)+searchLength > searchLength {
return false, 0
}
found := strings.Index(target[index:], search)
if 0 > found {
return false, 0
}
return true, uint(found)
}
func builtinString_split(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
separatorValue := call.Argument(0)
limitValue := call.Argument(1)
limit := -1
if limitValue.IsDefined() {
limit = int(toUint32(limitValue))
}
if limit == 0 {
return toValue_object(call.runtime.newArray(0))
}
if separatorValue.IsUndefined() {
return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)}))
}
if separatorValue.isRegExp() {
targetLength := len(target)
search := separatorValue._object().regExpValue().regularExpression
valueArray := []Value{}
result := search.FindAllStringSubmatchIndex(target, -1)
lastIndex := 0
found := 0
for _, match := range result {
if match[0] == match[1] {
// FIXME Ugh, this is a hack
if match[0] == 0 || match[0] == targetLength {
continue
}
}
if lastIndex != match[0] {
valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]]))
found++
} else if lastIndex == match[0] {
if lastIndex != -1 {
valueArray = append(valueArray, toValue_string(""))
found++
}
}
lastIndex = match[1]
if found == limit {
goto RETURN
}
captureCount := len(match) / 2
for index := 1; index < captureCount; index++ {
offset := index * 2
value := Value{}
if match[offset] != -1 {
value = toValue_string(target[match[offset]:match[offset+1]])
}
valueArray = append(valueArray, value)
found++
if found == limit {
goto RETURN
}
}
}
if found != limit {
if lastIndex != targetLength {
valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength]))
} else {
valueArray = append(valueArray, toValue_string(""))
}
}
RETURN:
return toValue_object(call.runtime.newArrayOf(valueArray))
} else {
separator := separatorValue.string()
splitLimit := limit
excess := false
if limit > 0 {
splitLimit = limit + 1
excess = true
}
split := strings.SplitN(target, separator, splitLimit)
if excess && len(split) > limit {
split = split[:limit]
}
valueArray := make([]Value, len(split))
for index, value := range split {
valueArray[index] = toValue_string(value)
}
return toValue_object(call.runtime.newArrayOf(valueArray))
}
}
func builtinString_slice(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
length := int64(len(target))
start, end := rangeStartEnd(call.ArgumentList, length, false)
if end-start <= 0 {
return toValue_string("")
}
return toValue_string(target[start:end])
}
func builtinString_substring(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()
length := int64(len(target))
start, end := rangeStartEnd(call.ArgumentList, length, true)
if start > end {
start, end = end, start
}
return toValue_string(target[start:end])
}
func builtinString_substr(call FunctionCall) Value {
target := call.This.string()
size := int64(len(target))
start, length := rangeStartLength(call.ArgumentList, size)
if start >= size {
return toValue_string("")
}
if length <= 0 {
return toValue_string("")
}
if start+length >= size {
// Cap length to be to the end of the string
// start = 3, length = 5, size = 4 [0, 1, 2, 3]
// 4 - 3 = 1
// target[3:4]
length = size - start
}
return toValue_string(target[start : start+length])
}
func builtinString_toLowerCase(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
return toValue_string(strings.ToLower(call.This.string()))
}
func builtinString_toUpperCase(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
return toValue_string(strings.ToUpper(call.This.string()))
}
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
func builtinString_trim(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.Trim(call.This.string(),
builtinString_trim_whitespace))
}
// Mozilla extension, not ECMAScript 5
func builtinString_trimLeft(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.TrimLeft(call.This.string(),
builtinString_trim_whitespace))
}
// Mozilla extension, not ECMAScript 5
func builtinString_trimRight(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
return toValue(strings.TrimRight(call.This.string(),
builtinString_trim_whitespace))
}
func builtinString_localeCompare(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
this := call.This.string()
that := call.Argument(0).string()
if this < that {
return toValue_int(-1)
} else if this == that {
return toValue_int(0)
}
return toValue_int(1)
}
/*
An alternate version of String.trim
func builtinString_trim(call FunctionCall) Value {
checkObjectCoercible(call.This)
return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator))
}
*/
func builtinString_toLocaleLowerCase(call FunctionCall) Value {
return builtinString_toLowerCase(call)
}
func builtinString_toLocaleUpperCase(call FunctionCall) Value {
return builtinString_toUpperCase(call)
}

View file

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

View file

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

@ -1,96 +0,0 @@
package otto
import (
"strconv"
)
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value {
if !eval {
self.enterGlobalScope()
defer func() {
self.leaveScope()
}()
}
self.cmpl_functionDeclaration(node.functionList)
self.cmpl_variableDeclaration(node.varList)
self.scope.frame.file = node.file
return self.cmpl_evaluate_nodeStatementList(node.body)
}
func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
indexOfParameterName := make([]string, len(argumentList))
// function(abc, def, ghi)
// indexOfParameterName[0] = "abc"
// indexOfParameterName[1] = "def"
// indexOfParameterName[2] = "ghi"
// ...
argumentsFound := false
for index, name := range node.parameterList {
if name == "arguments" {
argumentsFound = true
}
value := Value{}
if index < len(argumentList) {
value = argumentList[index]
indexOfParameterName[index] = name
}
// strict = false
self.scope.lexical.setValue(name, value, false)
}
if !argumentsFound {
arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList))
arguments.defineProperty("callee", toValue_object(function), 0101, false)
stash.arguments = arguments
// strict = false
self.scope.lexical.setValue("arguments", toValue_object(arguments), false)
for index, _ := range argumentList {
if index < len(node.parameterList) {
continue
}
indexAsString := strconv.FormatInt(int64(index), 10)
arguments.defineProperty(indexAsString, argumentList[index], 0111, false)
}
}
self.cmpl_functionDeclaration(node.functionList)
self.cmpl_variableDeclaration(node.varList)
result := self.cmpl_evaluate_nodeStatement(node.body)
if result.kind == valueResult {
return result
}
return Value{}
}
func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) {
executionContext := self.scope
eval := executionContext.eval
stash := executionContext.variable
for _, function := range list {
name := function.name
value := self.cmpl_evaluate_nodeExpression(function)
if !stash.hasBinding(name) {
stash.createBinding(name, eval == true, value)
} else {
// TODO 10.5.5.e
stash.setBinding(name, value, false) // TODO strict
}
}
}
func (self *_runtime) cmpl_variableDeclaration(list []string) {
executionContext := self.scope
eval := executionContext.eval
stash := executionContext.variable
for _, name := range list {
if !stash.hasBinding(name) {
stash.createBinding(name, eval == true, Value{}) // TODO strict?
}
}
}

View file

@ -1,460 +0,0 @@
package otto
import (
"fmt"
"math"
"runtime"
"github.com/robertkrimen/otto/token"
)
func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
// Allow interpreter interruption
// If the Interrupt channel is nil, then
// we avoid runtime.Gosched() overhead (if any)
// FIXME: Test this
if self.otto.Interrupt != nil {
runtime.Gosched()
select {
case value := <-self.otto.Interrupt:
value()
default:
}
}
switch node := node.(type) {
case *_nodeArrayLiteral:
return self.cmpl_evaluate_nodeArrayLiteral(node)
case *_nodeAssignExpression:
return self.cmpl_evaluate_nodeAssignExpression(node)
case *_nodeBinaryExpression:
if node.comparison {
return self.cmpl_evaluate_nodeBinaryExpression_comparison(node)
} else {
return self.cmpl_evaluate_nodeBinaryExpression(node)
}
case *_nodeBracketExpression:
return self.cmpl_evaluate_nodeBracketExpression(node)
case *_nodeCallExpression:
return self.cmpl_evaluate_nodeCallExpression(node, nil)
case *_nodeConditionalExpression:
return self.cmpl_evaluate_nodeConditionalExpression(node)
case *_nodeDotExpression:
return self.cmpl_evaluate_nodeDotExpression(node)
case *_nodeFunctionLiteral:
var local = self.scope.lexical
if node.name != "" {
local = self.newDeclarationStash(local)
}
value := toValue_object(self.newNodeFunction(node, local))
if node.name != "" {
local.createBinding(node.name, false, value)
}
return value
case *_nodeIdentifier:
name := node.name
// TODO Should be true or false (strictness) depending on context
// getIdentifierReference should not return nil, but we check anyway and panic
// so as not to propagate the nil into something else
reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx))
if reference == nil {
// Should never get here!
panic(hereBeDragons("referenceError == nil: " + name))
}
return toValue(reference)
case *_nodeLiteral:
return node.value
case *_nodeNewExpression:
return self.cmpl_evaluate_nodeNewExpression(node)
case *_nodeObjectLiteral:
return self.cmpl_evaluate_nodeObjectLiteral(node)
case *_nodeRegExpLiteral:
return toValue_object(self._newRegExp(node.pattern, node.flags))
case *_nodeSequenceExpression:
return self.cmpl_evaluate_nodeSequenceExpression(node)
case *_nodeThisExpression:
return toValue_object(self.scope.this)
case *_nodeUnaryExpression:
return self.cmpl_evaluate_nodeUnaryExpression(node)
case *_nodeVariableExpression:
return self.cmpl_evaluate_nodeVariableExpression(node)
}
panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node))
}
func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value {
valueArray := []Value{}
for _, node := range node.value {
if node == nil {
valueArray = append(valueArray, emptyValue)
} else {
valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve())
}
}
result := self.newArrayOf(valueArray)
return toValue_object(result)
}
func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value {
left := self.cmpl_evaluate_nodeExpression(node.left)
right := self.cmpl_evaluate_nodeExpression(node.right)
rightValue := right.resolve()
result := rightValue
if node.operator != token.ASSIGN {
result = self.calculateBinaryExpression(node.operator, left, rightValue)
}
self.putValue(left.reference(), result)
return result
}
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value {
left := self.cmpl_evaluate_nodeExpression(node.left)
leftValue := left.resolve()
switch node.operator {
// Logical
case token.LOGICAL_AND:
if !leftValue.bool() {
return leftValue
}
right := self.cmpl_evaluate_nodeExpression(node.right)
return right.resolve()
case token.LOGICAL_OR:
if leftValue.bool() {
return leftValue
}
right := self.cmpl_evaluate_nodeExpression(node.right)
return right.resolve()
}
return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right))
}
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value {
left := self.cmpl_evaluate_nodeExpression(node.left).resolve()
right := self.cmpl_evaluate_nodeExpression(node.right).resolve()
return toValue_bool(self.calculateComparison(node.operator, left, right))
}
func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value {
target := self.cmpl_evaluate_nodeExpression(node.left)
targetValue := target.resolve()
member := self.cmpl_evaluate_nodeExpression(node.member)
memberValue := member.resolve()
// TODO Pass in base value as-is, and defer toObject till later?
object, err := self.objectCoerce(targetValue)
if err != nil {
panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx)))
}
return toValue(newPropertyReference(self, object, memberValue.string(), false, _at(node.idx)))
}
func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value {
rt := self
this := Value{}
callee := self.cmpl_evaluate_nodeExpression(node.callee)
argumentList := []Value{}
if withArgumentList != nil {
argumentList = self.toValueArray(withArgumentList...)
} else {
for _, argumentNode := range node.argumentList {
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
}
}
rf := callee.reference()
vl := callee.resolve()
eval := false // Whether this call is a (candidate for) direct call to eval
name := ""
if rf != nil {
switch rf := rf.(type) {
case *_propertyReference:
name = rf.name
object := rf.base
this = toValue_object(object)
eval = rf.name == "eval" // Possible direct eval
case *_stashReference:
// TODO ImplicitThisValue
name = rf.name
eval = rf.name == "eval" // Possible direct eval
default:
// FIXME?
panic(rt.panicTypeError("Here be dragons"))
}
}
at := _at(-1)
switch callee := node.callee.(type) {
case *_nodeIdentifier:
at = _at(callee.idx)
case *_nodeDotExpression:
at = _at(callee.idx)
case *_nodeBracketExpression:
at = _at(callee.idx)
}
frame := _frame{
callee: name,
file: self.scope.frame.file,
}
if !vl.IsFunction() {
if name == "" {
// FIXME Maybe typeof?
panic(rt.panicTypeError("%v is not a function", vl, at))
}
panic(rt.panicTypeError("'%s' is not a function", name, at))
}
self.scope.frame.offset = int(at)
return vl._object().call(this, argumentList, eval, frame)
}
func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value {
test := self.cmpl_evaluate_nodeExpression(node.test)
testValue := test.resolve()
if testValue.bool() {
return self.cmpl_evaluate_nodeExpression(node.consequent)
}
return self.cmpl_evaluate_nodeExpression(node.alternate)
}
func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value {
target := self.cmpl_evaluate_nodeExpression(node.left)
targetValue := target.resolve()
// TODO Pass in base value as-is, and defer toObject till later?
object, err := self.objectCoerce(targetValue)
if err != nil {
panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), _at(node.idx)))
}
return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx)))
}
func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value {
rt := self
callee := self.cmpl_evaluate_nodeExpression(node.callee)
argumentList := []Value{}
for _, argumentNode := range node.argumentList {
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
}
rf := callee.reference()
vl := callee.resolve()
name := ""
if rf != nil {
switch rf := rf.(type) {
case *_propertyReference:
name = rf.name
case *_stashReference:
name = rf.name
default:
panic(rt.panicTypeError("Here be dragons"))
}
}
at := _at(-1)
switch callee := node.callee.(type) {
case *_nodeIdentifier:
at = _at(callee.idx)
case *_nodeDotExpression:
at = _at(callee.idx)
case *_nodeBracketExpression:
at = _at(callee.idx)
}
if !vl.IsFunction() {
if name == "" {
// FIXME Maybe typeof?
panic(rt.panicTypeError("%v is not a function", vl, at))
}
panic(rt.panicTypeError("'%s' is not a function", name, at))
}
self.scope.frame.offset = int(at)
return vl._object().construct(argumentList)
}
func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value {
result := self.newObject()
for _, property := range node.value {
switch property.kind {
case "value":
result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false)
case "get":
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
descriptor := _property{}
descriptor.mode = 0211
descriptor.value = _propertyGetSet{getter, nil}
result.defineOwnProperty(property.key, descriptor, false)
case "set":
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
descriptor := _property{}
descriptor.mode = 0211
descriptor.value = _propertyGetSet{nil, setter}
result.defineOwnProperty(property.key, descriptor, false)
default:
panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind))
}
}
return toValue_object(result)
}
func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value {
var result Value
for _, node := range node.sequence {
result = self.cmpl_evaluate_nodeExpression(node)
result = result.resolve()
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value {
target := self.cmpl_evaluate_nodeExpression(node.operand)
switch node.operator {
case token.TYPEOF, token.DELETE:
if target.kind == valueReference && target.reference().invalid() {
if node.operator == token.TYPEOF {
return toValue_string("undefined")
}
return trueValue
}
}
switch node.operator {
case token.NOT:
targetValue := target.resolve()
if targetValue.bool() {
return falseValue
}
return trueValue
case token.BITWISE_NOT:
targetValue := target.resolve()
integerValue := toInt32(targetValue)
return toValue_int32(^integerValue)
case token.PLUS:
targetValue := target.resolve()
return toValue_float64(targetValue.float64())
case token.MINUS:
targetValue := target.resolve()
value := targetValue.float64()
// TODO Test this
sign := float64(-1)
if math.Signbit(value) {
sign = 1
}
return toValue_float64(math.Copysign(value, sign))
case token.INCREMENT:
targetValue := target.resolve()
if node.postfix {
// Postfix++
oldValue := targetValue.float64()
newValue := toValue_float64(+1 + oldValue)
self.putValue(target.reference(), newValue)
return toValue_float64(oldValue)
} else {
// ++Prefix
newValue := toValue_float64(+1 + targetValue.float64())
self.putValue(target.reference(), newValue)
return newValue
}
case token.DECREMENT:
targetValue := target.resolve()
if node.postfix {
// Postfix--
oldValue := targetValue.float64()
newValue := toValue_float64(-1 + oldValue)
self.putValue(target.reference(), newValue)
return toValue_float64(oldValue)
} else {
// --Prefix
newValue := toValue_float64(-1 + targetValue.float64())
self.putValue(target.reference(), newValue)
return newValue
}
case token.VOID:
target.resolve() // FIXME Side effect?
return Value{}
case token.DELETE:
reference := target.reference()
if reference == nil {
return trueValue
}
return toValue_bool(target.reference().delete())
case token.TYPEOF:
targetValue := target.resolve()
switch targetValue.kind {
case valueUndefined:
return toValue_string("undefined")
case valueNull:
return toValue_string("object")
case valueBoolean:
return toValue_string("boolean")
case valueNumber:
return toValue_string("number")
case valueString:
return toValue_string("string")
case valueObject:
if targetValue._object().isCall() {
return toValue_string("function")
}
return toValue_string("object")
default:
// FIXME ?
}
}
panic(hereBeDragons())
}
func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value {
if node.initializer != nil {
// FIXME If reference is nil
left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx))
right := self.cmpl_evaluate_nodeExpression(node.initializer)
rightValue := right.resolve()
self.putValue(left, rightValue)
}
return toValue_string(node.name)
}

View file

@ -1,424 +0,0 @@
package otto
import (
"fmt"
"runtime"
"github.com/robertkrimen/otto/token"
)
func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
// Allow interpreter interruption
// If the Interrupt channel is nil, then
// we avoid runtime.Gosched() overhead (if any)
// FIXME: Test this
if self.otto.Interrupt != nil {
runtime.Gosched()
select {
case value := <-self.otto.Interrupt:
value()
default:
}
}
switch node := node.(type) {
case *_nodeBlockStatement:
labels := self.labels
self.labels = nil
value := self.cmpl_evaluate_nodeStatementList(node.list)
switch value.kind {
case valueResult:
switch value.evaluateBreak(labels) {
case resultBreak:
return emptyValue
}
}
return value
case *_nodeBranchStatement:
target := node.label
switch node.branch { // FIXME Maybe node.kind? node.operator?
case token.BREAK:
return toValue(newBreakResult(target))
case token.CONTINUE:
return toValue(newContinueResult(target))
}
case *_nodeDebuggerStatement:
if self.debugger != nil {
self.debugger(self.otto)
}
return emptyValue // Nothing happens.
case *_nodeDoWhileStatement:
return self.cmpl_evaluate_nodeDoWhileStatement(node)
case *_nodeEmptyStatement:
return emptyValue
case *_nodeExpressionStatement:
return self.cmpl_evaluate_nodeExpression(node.expression)
case *_nodeForInStatement:
return self.cmpl_evaluate_nodeForInStatement(node)
case *_nodeForStatement:
return self.cmpl_evaluate_nodeForStatement(node)
case *_nodeIfStatement:
return self.cmpl_evaluate_nodeIfStatement(node)
case *_nodeLabelledStatement:
self.labels = append(self.labels, node.label)
defer func() {
if len(self.labels) > 0 {
self.labels = self.labels[:len(self.labels)-1] // Pop the label
} else {
self.labels = nil
}
}()
return self.cmpl_evaluate_nodeStatement(node.statement)
case *_nodeReturnStatement:
if node.argument != nil {
return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve()))
}
return toValue(newReturnResult(Value{}))
case *_nodeSwitchStatement:
return self.cmpl_evaluate_nodeSwitchStatement(node)
case *_nodeThrowStatement:
value := self.cmpl_evaluate_nodeExpression(node.argument).resolve()
panic(newException(value))
case *_nodeTryStatement:
return self.cmpl_evaluate_nodeTryStatement(node)
case *_nodeVariableStatement:
// Variables are already defined, this is initialization only
for _, variable := range node.list {
self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression))
}
return emptyValue
case *_nodeWhileStatement:
return self.cmpl_evaluate_nodeWhileStatement(node)
case *_nodeWithStatement:
return self.cmpl_evaluate_nodeWithStatement(node)
}
panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node))
}
func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value {
var result Value
for _, node := range list {
value := self.cmpl_evaluate_nodeStatement(node)
switch value.kind {
case valueResult:
return value
case valueEmpty:
default:
// We have getValue here to (for example) trigger a
// ReferenceError (of the not defined variety)
// Not sure if this is the best way to error out early
// for such errors or if there is a better way
// TODO Do we still need this?
result = value.resolve()
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value {
labels := append(self.labels, "")
self.labels = nil
test := node.test
result := emptyValue
resultBreak:
for {
for _, node := range node.body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
return value
case resultBreak:
break resultBreak
case resultContinue:
goto resultContinue
}
case valueEmpty:
default:
result = value
}
}
resultContinue:
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
// Stahp: do ... while (false)
break
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value {
labels := append(self.labels, "")
self.labels = nil
source := self.cmpl_evaluate_nodeExpression(node.source)
sourceValue := source.resolve()
switch sourceValue.kind {
case valueUndefined, valueNull:
return emptyValue
}
sourceObject := self.toObject(sourceValue)
into := node.into
body := node.body
result := emptyValue
object := sourceObject
for object != nil {
enumerateValue := emptyValue
object.enumerate(false, func(name string) bool {
into := self.cmpl_evaluate_nodeExpression(into)
// In the case of: for (var abc in def) ...
if into.reference() == nil {
identifier := into.string()
// TODO Should be true or false (strictness) depending on context
into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1))
}
self.putValue(into.reference(), toValue_string(name))
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
enumerateValue = value
return false
case resultBreak:
object = nil
return false
case resultContinue:
return true
}
case valueEmpty:
default:
enumerateValue = value
}
}
return true
})
if object == nil {
break
}
object = object.prototype
if !enumerateValue.isEmpty() {
result = enumerateValue
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value {
labels := append(self.labels, "")
self.labels = nil
initializer := node.initializer
test := node.test
update := node.update
body := node.body
if initializer != nil {
initialResult := self.cmpl_evaluate_nodeExpression(initializer)
initialResult.resolve() // Side-effect trigger
}
result := emptyValue
resultBreak:
for {
if test != nil {
testResult := self.cmpl_evaluate_nodeExpression(test)
testResultValue := testResult.resolve()
if testResultValue.bool() == false {
break
}
}
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
return value
case resultBreak:
break resultBreak
case resultContinue:
goto resultContinue
}
case valueEmpty:
default:
result = value
}
}
resultContinue:
if update != nil {
updateResult := self.cmpl_evaluate_nodeExpression(update)
updateResult.resolve() // Side-effect trigger
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value {
test := self.cmpl_evaluate_nodeExpression(node.test)
testValue := test.resolve()
if testValue.bool() {
return self.cmpl_evaluate_nodeStatement(node.consequent)
} else if node.alternate != nil {
return self.cmpl_evaluate_nodeStatement(node.alternate)
}
return emptyValue
}
func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value {
labels := append(self.labels, "")
self.labels = nil
discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant)
target := node.default_
for index, clause := range node.body {
test := clause.test
if test != nil {
if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) {
target = index
break
}
}
}
result := emptyValue
if target != -1 {
for _, clause := range node.body[target:] {
for _, statement := range clause.consequent {
value := self.cmpl_evaluate_nodeStatement(statement)
switch value.kind {
case valueResult:
switch value.evaluateBreak(labels) {
case resultReturn:
return value
case resultBreak:
return emptyValue
}
case valueEmpty:
default:
result = value
}
}
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value {
tryCatchValue, exception := self.tryCatchEvaluate(func() Value {
return self.cmpl_evaluate_nodeStatement(node.body)
})
if exception && node.catch != nil {
outer := self.scope.lexical
self.scope.lexical = self.newDeclarationStash(outer)
defer func() {
self.scope.lexical = outer
}()
// TODO If necessary, convert TypeError<runtime> => TypeError
// That, is, such errors can be thrown despite not being JavaScript "native"
// strict = false
self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false)
// FIXME node.CatchParameter
// FIXME node.Catch
tryCatchValue, exception = self.tryCatchEvaluate(func() Value {
return self.cmpl_evaluate_nodeStatement(node.catch.body)
})
}
if node.finally != nil {
finallyValue := self.cmpl_evaluate_nodeStatement(node.finally)
if finallyValue.kind == valueResult {
return finallyValue
}
}
if exception {
panic(newException(tryCatchValue))
}
return tryCatchValue
}
func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value {
test := node.test
body := node.body
labels := append(self.labels, "")
self.labels = nil
result := emptyValue
resultBreakContinue:
for {
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
// Stahp: while (false) ...
break
}
for _, node := range body {
value := self.cmpl_evaluate_nodeStatement(node)
switch value.kind {
case valueResult:
switch value.evaluateBreakContinue(labels) {
case resultReturn:
return value
case resultBreak:
break resultBreakContinue
case resultContinue:
continue resultBreakContinue
}
case valueEmpty:
default:
result = value
}
}
}
return result
}
func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value {
object := self.cmpl_evaluate_nodeExpression(node.object)
outer := self.scope.lexical
lexical := self.newObjectStash(self.toObject(object.resolve()), outer)
self.scope.lexical = lexical
defer func() {
self.scope.lexical = outer
}()
return self.cmpl_evaluate_nodeStatement(node.body)
}

View file

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

View file

@ -1,51 +0,0 @@
package otto
import (
"fmt"
"os"
"strings"
)
func formatForConsole(argumentList []Value) string {
output := []string{}
for _, argument := range argumentList {
output = append(output, fmt.Sprintf("%v", argument))
}
return strings.Join(output, " ")
}
func builtinConsole_log(call FunctionCall) Value {
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
return Value{}
}
func builtinConsole_error(call FunctionCall) Value {
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
return Value{}
}
// Nothing happens.
func builtinConsole_dir(call FunctionCall) Value {
return Value{}
}
func builtinConsole_time(call FunctionCall) Value {
return Value{}
}
func builtinConsole_timeEnd(call FunctionCall) Value {
return Value{}
}
func builtinConsole_trace(call FunctionCall) Value {
return Value{}
}
func builtinConsole_assert(call FunctionCall) Value {
return Value{}
}
func (runtime *_runtime) newConsole() *_object {
return newConsoleObject(runtime)
}

View file

@ -1,9 +0,0 @@
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
package otto
import (
Dbg "github.com/robertkrimen/otto/dbg"
)
var dbg, dbgf = Dbg.New()

View file

@ -1,252 +0,0 @@
package otto
import (
"errors"
"fmt"
"github.com/robertkrimen/otto/file"
)
type _exception struct {
value interface{}
}
func newException(value interface{}) *_exception {
return &_exception{
value: value,
}
}
func (self *_exception) eject() interface{} {
value := self.value
self.value = nil // Prevent Go from holding on to the value, whatever it is
return value
}
type _error struct {
name string
message string
trace []_frame
offset int
}
func (err _error) format() string {
if len(err.name) == 0 {
return err.message
}
if len(err.message) == 0 {
return err.name
}
return fmt.Sprintf("%s: %s", err.name, err.message)
}
func (err _error) formatWithStack() string {
str := err.format() + "\n"
for _, frame := range err.trace {
str += " at " + frame.location() + "\n"
}
return str
}
type _frame struct {
native bool
nativeFile string
nativeLine int
file *file.File
offset int
callee string
}
var (
nativeFrame = _frame{}
)
type _at int
func (fr _frame) location() string {
str := "<unknown>"
switch {
case fr.native:
str = "<native code>"
if fr.nativeFile != "" && fr.nativeLine != 0 {
str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine)
}
case fr.file != nil:
if p := fr.file.Position(file.Idx(fr.offset)); p != nil {
path, line, column := p.Filename, p.Line, p.Column
if path == "" {
path = "<anonymous>"
}
str = fmt.Sprintf("%s:%d:%d", path, line, column)
}
}
if fr.callee != "" {
str = fmt.Sprintf("%s (%s)", fr.callee, str)
}
return str
}
// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
type Error struct {
_error
}
// Error returns a description of the error
//
// TypeError: 'def' is not a function
//
func (err Error) Error() string {
return err.format()
}
// String returns a description of the error and a trace of where the
// error occurred.
//
// TypeError: 'def' is not a function
// at xyz (<anonymous>:3:9)
// at <anonymous>:7:1/
//
func (err Error) String() string {
return err.formatWithStack()
}
func (err _error) describe(format string, in ...interface{}) string {
return fmt.Sprintf(format, in...)
}
func (self _error) messageValue() Value {
if self.message == "" {
return Value{}
}
return toValue_string(self.message)
}
func (rt *_runtime) typeErrorResult(throw bool) bool {
if throw {
panic(rt.panicTypeError())
}
return false
}
func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error {
err := _error{
name: name,
offset: -1,
}
description := ""
length := len(in)
if rt != nil && rt.scope != nil {
scope := rt.scope
for i := 0; i < stackFramesToPop; i++ {
if scope.outer != nil {
scope = scope.outer
}
}
frame := scope.frame
if length > 0 {
if at, ok := in[length-1].(_at); ok {
in = in[0 : length-1]
if scope != nil {
frame.offset = int(at)
}
length--
}
if length > 0 {
description, in = in[0].(string), in[1:]
}
}
limit := rt.traceLimit
err.trace = append(err.trace, frame)
if scope != nil {
for scope = scope.outer; scope != nil; scope = scope.outer {
if limit--; limit == 0 {
break
}
if scope.frame.offset >= 0 {
err.trace = append(err.trace, scope.frame)
}
}
}
} else {
if length > 0 {
description, in = in[0].(string), in[1:]
}
}
err.message = err.describe(description, in...)
return err
}
func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception {
return &_exception{
value: newError(rt, "TypeError", 0, argumentList...),
}
}
func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception {
return &_exception{
value: newError(rt, "ReferenceError", 0, argumentList...),
}
}
func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception {
return &_exception{
value: newError(rt, "URIError", 0, argumentList...),
}
}
func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception {
return &_exception{
value: newError(rt, "SyntaxError", 0, argumentList...),
}
}
func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception {
return &_exception{
value: newError(rt, "RangeError", 0, argumentList...),
}
}
func catchPanic(function func()) (err error) {
defer func() {
if caught := recover(); caught != nil {
if exception, ok := caught.(*_exception); ok {
caught = exception.eject()
}
switch caught := caught.(type) {
case *Error:
err = caught
return
case _error:
err = &Error{caught}
return
case Value:
if vl := caught._object(); vl != nil {
switch vl := vl.value.(type) {
case _error:
err = &Error{vl}
return
}
}
err = errors.New(caught.string())
return
}
panic(caught)
}
}()
function()
return nil
}

View file

@ -1,318 +0,0 @@
package otto
import (
"fmt"
"math"
"strings"
"github.com/robertkrimen/otto/token"
)
func (self *_runtime) evaluateMultiply(left float64, right float64) Value {
// TODO 11.5.1
return Value{}
}
func (self *_runtime) evaluateDivide(left float64, right float64) Value {
if math.IsNaN(left) || math.IsNaN(right) {
return NaNValue()
}
if math.IsInf(left, 0) && math.IsInf(right, 0) {
return NaNValue()
}
if left == 0 && right == 0 {
return NaNValue()
}
if math.IsInf(left, 0) {
if math.Signbit(left) == math.Signbit(right) {
return positiveInfinityValue()
} else {
return negativeInfinityValue()
}
}
if math.IsInf(right, 0) {
if math.Signbit(left) == math.Signbit(right) {
return positiveZeroValue()
} else {
return negativeZeroValue()
}
}
if right == 0 {
if math.Signbit(left) == math.Signbit(right) {
return positiveInfinityValue()
} else {
return negativeInfinityValue()
}
}
return toValue_float64(left / right)
}
func (self *_runtime) evaluateModulo(left float64, right float64) Value {
// TODO 11.5.3
return Value{}
}
func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value {
leftValue := left.resolve()
switch operator {
// Additive
case token.PLUS:
leftValue = toPrimitive(leftValue)
rightValue := right.resolve()
rightValue = toPrimitive(rightValue)
if leftValue.IsString() || rightValue.IsString() {
return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, ""))
} else {
return toValue_float64(leftValue.float64() + rightValue.float64())
}
case token.MINUS:
rightValue := right.resolve()
return toValue_float64(leftValue.float64() - rightValue.float64())
// Multiplicative
case token.MULTIPLY:
rightValue := right.resolve()
return toValue_float64(leftValue.float64() * rightValue.float64())
case token.SLASH:
rightValue := right.resolve()
return self.evaluateDivide(leftValue.float64(), rightValue.float64())
case token.REMAINDER:
rightValue := right.resolve()
return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64()))
// Logical
case token.LOGICAL_AND:
left := leftValue.bool()
if !left {
return falseValue
}
return toValue_bool(right.resolve().bool())
case token.LOGICAL_OR:
left := leftValue.bool()
if left {
return trueValue
}
return toValue_bool(right.resolve().bool())
// Bitwise
case token.AND:
rightValue := right.resolve()
return toValue_int32(toInt32(leftValue) & toInt32(rightValue))
case token.OR:
rightValue := right.resolve()
return toValue_int32(toInt32(leftValue) | toInt32(rightValue))
case token.EXCLUSIVE_OR:
rightValue := right.resolve()
return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue))
// Shift
// (Masking of 0x1f is to restrict the shift to a maximum of 31 places)
case token.SHIFT_LEFT:
rightValue := right.resolve()
return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f))
case token.SHIFT_RIGHT:
rightValue := right.resolve()
return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f))
case token.UNSIGNED_SHIFT_RIGHT:
rightValue := right.resolve()
// Shifting an unsigned integer is a logical shift
return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f))
case token.INSTANCEOF:
rightValue := right.resolve()
if !rightValue.IsObject() {
panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue))
}
return toValue_bool(rightValue._object().hasInstance(leftValue))
case token.IN:
rightValue := right.resolve()
if !rightValue.IsObject() {
panic(self.panicTypeError())
}
return toValue_bool(rightValue._object().hasProperty(leftValue.string()))
}
panic(hereBeDragons(operator))
}
func valueKindDispatchKey(left _valueKind, right _valueKind) int {
return (int(left) << 2) + int(right)
}
var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch()
func makeEqualDispatch() map[int](func(Value, Value) bool) {
key := valueKindDispatchKey
return map[int](func(Value, Value) bool){
key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() },
}
}
type _lessThanResult int
const (
lessThanFalse _lessThanResult = iota
lessThanTrue
lessThanUndefined
)
func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult {
x := Value{}
y := x
if leftFirst {
x = toNumberPrimitive(left)
y = toNumberPrimitive(right)
} else {
y = toNumberPrimitive(right)
x = toNumberPrimitive(left)
}
result := false
if x.kind != valueString || y.kind != valueString {
x, y := x.float64(), y.float64()
if math.IsNaN(x) || math.IsNaN(y) {
return lessThanUndefined
}
result = x < y
} else {
x, y := x.string(), y.string()
result = x < y
}
if result {
return lessThanTrue
}
return lessThanFalse
}
// FIXME Probably a map is not the most efficient way to do this
var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){
// <
map[_lessThanResult]bool{
lessThanFalse: false,
lessThanTrue: true,
lessThanUndefined: false,
},
// >
map[_lessThanResult]bool{
lessThanFalse: false,
lessThanTrue: true,
lessThanUndefined: false,
},
// <=
map[_lessThanResult]bool{
lessThanFalse: true,
lessThanTrue: false,
lessThanUndefined: false,
},
// >=
map[_lessThanResult]bool{
lessThanFalse: true,
lessThanTrue: false,
lessThanUndefined: false,
},
}
func (self *_runtime) calculateComparison(comparator token.Token, left Value, right Value) bool {
// FIXME Use strictEqualityComparison?
// TODO This might be redundant now (with regards to evaluateComparison)
x := left.resolve()
y := right.resolve()
kindEqualKind := false
result := true
negate := false
switch comparator {
case token.LESS:
result = lessThanTable[0][calculateLessThan(x, y, true)]
case token.GREATER:
result = lessThanTable[1][calculateLessThan(y, x, false)]
case token.LESS_OR_EQUAL:
result = lessThanTable[2][calculateLessThan(y, x, false)]
case token.GREATER_OR_EQUAL:
result = lessThanTable[3][calculateLessThan(x, y, true)]
case token.STRICT_NOT_EQUAL:
negate = true
fallthrough
case token.STRICT_EQUAL:
if x.kind != y.kind {
result = false
} else {
kindEqualKind = true
}
case token.NOT_EQUAL:
negate = true
fallthrough
case token.EQUAL:
if x.kind == y.kind {
kindEqualKind = true
} else if x.kind <= valueNull && y.kind <= valueNull {
result = true
} else if x.kind <= valueNull || y.kind <= valueNull {
result = false
} else if x.kind <= valueString && y.kind <= valueString {
result = x.float64() == y.float64()
} else if x.kind == valueBoolean {
result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y)
} else if y.kind == valueBoolean {
result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64()))
} else if x.kind == valueObject {
result = self.calculateComparison(token.EQUAL, toPrimitive(x), y)
} else if y.kind == valueObject {
result = self.calculateComparison(token.EQUAL, x, toPrimitive(y))
} else {
panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y))
}
default:
panic(fmt.Errorf("Unknown comparator %s", comparator.String()))
}
if kindEqualKind {
switch x.kind {
case valueUndefined, valueNull:
result = true
case valueNumber:
x := x.float64()
y := y.float64()
if math.IsNaN(x) || math.IsNaN(y) {
result = false
} else {
result = x == y
}
case valueString:
result = x.string() == y.string()
case valueBoolean:
result = x.bool() == y.bool()
case valueObject:
result = x._object() == y._object()
default:
goto ERROR
}
}
if negate {
result = !result
}
return result
ERROR:
panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind))
}

View file

@ -1,221 +0,0 @@
package otto
import (
"strconv"
"time"
)
var (
prototypeValueObject = interface{}(nil)
prototypeValueFunction = _nativeFunctionObject{
call: func(_ FunctionCall) Value {
return Value{}
},
}
prototypeValueString = _stringASCII("")
// TODO Make this just false?
prototypeValueBoolean = Value{
kind: valueBoolean,
value: false,
}
prototypeValueNumber = Value{
kind: valueNumber,
value: 0,
}
prototypeValueDate = _dateObject{
epoch: 0,
isNaN: false,
time: time.Unix(0, 0).UTC(),
value: Value{
kind: valueNumber,
value: 0,
},
}
prototypeValueRegExp = _regExpObject{
regularExpression: nil,
global: false,
ignoreCase: false,
multiline: false,
source: "",
flags: "",
}
)
func newContext() *_runtime {
self := &_runtime{}
self.globalStash = self.newObjectStash(nil, nil)
self.globalObject = self.globalStash.object
_newContext(self)
self.eval = self.globalObject.property["eval"].value.(Value).value.(*_object)
self.globalObject.prototype = self.global.ObjectPrototype
return self
}
func (runtime *_runtime) newBaseObject() *_object {
self := newObject(runtime, "")
return self
}
func (runtime *_runtime) newClassObject(class string) *_object {
return newObject(runtime, class)
}
func (runtime *_runtime) newPrimitiveObject(class string, value Value) *_object {
self := runtime.newClassObject(class)
self.value = value
return self
}
func (self *_object) primitiveValue() Value {
switch value := self.value.(type) {
case Value:
return value
case _stringObject:
return toValue_string(value.String())
}
return Value{}
}
func (self *_object) hasPrimitive() bool {
switch self.value.(type) {
case Value, _stringObject:
return true
}
return false
}
func (runtime *_runtime) newObject() *_object {
self := runtime.newClassObject("Object")
self.prototype = runtime.global.ObjectPrototype
return self
}
func (runtime *_runtime) newArray(length uint32) *_object {
self := runtime.newArrayObject(length)
self.prototype = runtime.global.ArrayPrototype
return self
}
func (runtime *_runtime) newArrayOf(valueArray []Value) *_object {
self := runtime.newArray(uint32(len(valueArray)))
for index, value := range valueArray {
if value.isEmpty() {
continue
}
self.defineProperty(strconv.FormatInt(int64(index), 10), value, 0111, false)
}
return self
}
func (runtime *_runtime) newString(value Value) *_object {
self := runtime.newStringObject(value)
self.prototype = runtime.global.StringPrototype
return self
}
func (runtime *_runtime) newBoolean(value Value) *_object {
self := runtime.newBooleanObject(value)
self.prototype = runtime.global.BooleanPrototype
return self
}
func (runtime *_runtime) newNumber(value Value) *_object {
self := runtime.newNumberObject(value)
self.prototype = runtime.global.NumberPrototype
return self
}
func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_object {
pattern := ""
flags := ""
if object := patternValue._object(); object != nil && object.class == "RegExp" {
if flagsValue.IsDefined() {
panic(runtime.panicTypeError("Cannot supply flags when constructing one RegExp from another"))
}
regExp := object.regExpValue()
pattern = regExp.source
flags = regExp.flags
} else {
if patternValue.IsDefined() {
pattern = patternValue.string()
}
if flagsValue.IsDefined() {
flags = flagsValue.string()
}
}
return runtime._newRegExp(pattern, flags)
}
func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object {
self := runtime.newRegExpObject(pattern, flags)
self.prototype = runtime.global.RegExpPrototype
return self
}
// TODO Should (probably) be one argument, right? This is redundant
func (runtime *_runtime) newDate(epoch float64) *_object {
self := runtime.newDateObject(epoch)
self.prototype = runtime.global.DatePrototype
return self
}
func (runtime *_runtime) newError(name string, message Value, stackFramesToPop int) *_object {
var self *_object
switch name {
case "EvalError":
return runtime.newEvalError(message)
case "TypeError":
return runtime.newTypeError(message)
case "RangeError":
return runtime.newRangeError(message)
case "ReferenceError":
return runtime.newReferenceError(message)
case "SyntaxError":
return runtime.newSyntaxError(message)
case "URIError":
return runtime.newURIError(message)
}
self = runtime.newErrorObject(name, message, stackFramesToPop)
self.prototype = runtime.global.ErrorPrototype
if name != "" {
self.defineProperty("name", toValue_string(name), 0111, false)
}
return self
}
func (runtime *_runtime) newNativeFunction(name, file string, line int, _nativeFunction _nativeFunction) *_object {
self := runtime.newNativeFunctionObject(name, file, line, _nativeFunction, 0)
self.prototype = runtime.global.FunctionPrototype
prototype := runtime.newObject()
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
return self
}
func (runtime *_runtime) newNodeFunction(node *_nodeFunctionLiteral, scopeEnvironment _stash) *_object {
// TODO Implement 13.2 fully
self := runtime.newNodeFunctionObject(node, scopeEnvironment)
self.prototype = runtime.global.FunctionPrototype
prototype := runtime.newObject()
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
prototype.defineProperty("constructor", toValue_object(self), 0101, false)
return self
}
// FIXME Only in one place...
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object {
self := runtime.newBoundFunctionObject(target, this, argumentList)
self.prototype = runtime.global.FunctionPrototype
prototype := runtime.newObject()
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
return self
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,156 +0,0 @@
package otto
type _object struct {
runtime *_runtime
class string
objectClass *_objectClass
value interface{}
prototype *_object
extensible bool
property map[string]_property
propertyOrder []string
}
func newObject(runtime *_runtime, class string) *_object {
self := &_object{
runtime: runtime,
class: class,
objectClass: _classObject,
property: make(map[string]_property),
extensible: true,
}
return self
}
// 8.12
// 8.12.1
func (self *_object) getOwnProperty(name string) *_property {
return self.objectClass.getOwnProperty(self, name)
}
// 8.12.2
func (self *_object) getProperty(name string) *_property {
return self.objectClass.getProperty(self, name)
}
// 8.12.3
func (self *_object) get(name string) Value {
return self.objectClass.get(self, name)
}
// 8.12.4
func (self *_object) canPut(name string) bool {
return self.objectClass.canPut(self, name)
}
// 8.12.5
func (self *_object) put(name string, value Value, throw bool) {
self.objectClass.put(self, name, value, throw)
}
// 8.12.6
func (self *_object) hasProperty(name string) bool {
return self.objectClass.hasProperty(self, name)
}
func (self *_object) hasOwnProperty(name string) bool {
return self.objectClass.hasOwnProperty(self, name)
}
type _defaultValueHint int
const (
defaultValueNoHint _defaultValueHint = iota
defaultValueHintString
defaultValueHintNumber
)
// 8.12.8
func (self *_object) DefaultValue(hint _defaultValueHint) Value {
if hint == defaultValueNoHint {
if self.class == "Date" {
// Date exception
hint = defaultValueHintString
} else {
hint = defaultValueHintNumber
}
}
methodSequence := []string{"valueOf", "toString"}
if hint == defaultValueHintString {
methodSequence = []string{"toString", "valueOf"}
}
for _, methodName := range methodSequence {
method := self.get(methodName)
// FIXME This is redundant...
if method.isCallable() {
result := method._object().call(toValue_object(self), nil, false, nativeFrame)
if result.IsPrimitive() {
return result
}
}
}
panic(self.runtime.panicTypeError())
}
func (self *_object) String() string {
return self.DefaultValue(defaultValueHintString).string()
}
func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool {
return self.defineOwnProperty(name, _property{value, mode}, throw)
}
// 8.12.9
func (self *_object) defineOwnProperty(name string, descriptor _property, throw bool) bool {
return self.objectClass.defineOwnProperty(self, name, descriptor, throw)
}
func (self *_object) delete(name string, throw bool) bool {
return self.objectClass.delete(self, name, throw)
}
func (self *_object) enumerate(all bool, each func(string) bool) {
self.objectClass.enumerate(self, all, each)
}
func (self *_object) _exists(name string) bool {
_, exists := self.property[name]
return exists
}
func (self *_object) _read(name string) (_property, bool) {
property, exists := self.property[name]
return property, exists
}
func (self *_object) _write(name string, value interface{}, mode _propertyMode) {
if value == nil {
value = Value{}
}
_, exists := self.property[name]
self.property[name] = _property{value, mode}
if !exists {
self.propertyOrder = append(self.propertyOrder, name)
}
}
func (self *_object) _delete(name string) {
_, exists := self.property[name]
delete(self.property, name)
if exists {
for index, property := range self.propertyOrder {
if name == property {
if index == len(self.propertyOrder)-1 {
self.propertyOrder = self.propertyOrder[:index]
} else {
self.propertyOrder = append(self.propertyOrder[:index], self.propertyOrder[index+1:]...)
}
}
}
}
}

View file

@ -1,493 +0,0 @@
package otto
import (
"encoding/json"
)
type _objectClass struct {
getOwnProperty func(*_object, string) *_property
getProperty func(*_object, string) *_property
get func(*_object, string) Value
canPut func(*_object, string) bool
put func(*_object, string, Value, bool)
hasProperty func(*_object, string) bool
hasOwnProperty func(*_object, string) bool
defineOwnProperty func(*_object, string, _property, bool) bool
delete func(*_object, string, bool) bool
enumerate func(*_object, bool, func(string) bool)
clone func(*_object, *_object, *_clone) *_object
marshalJSON func(*_object) json.Marshaler
}
func objectEnumerate(self *_object, all bool, each func(string) bool) {
for _, name := range self.propertyOrder {
if all || self.property[name].enumerable() {
if !each(name) {
return
}
}
}
}
var (
_classObject,
_classArray,
_classString,
_classArguments,
_classGoStruct,
_classGoMap,
_classGoArray,
_classGoSlice,
_ *_objectClass
)
func init() {
_classObject = &_objectClass{
objectGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
objectEnumerate,
objectClone,
nil,
}
_classArray = &_objectClass{
objectGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
arrayDefineOwnProperty,
objectDelete,
objectEnumerate,
objectClone,
nil,
}
_classString = &_objectClass{
stringGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
stringEnumerate,
objectClone,
nil,
}
_classArguments = &_objectClass{
argumentsGetOwnProperty,
objectGetProperty,
argumentsGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
argumentsDefineOwnProperty,
argumentsDelete,
objectEnumerate,
objectClone,
nil,
}
_classGoStruct = &_objectClass{
goStructGetOwnProperty,
objectGetProperty,
objectGet,
goStructCanPut,
goStructPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
goStructEnumerate,
objectClone,
goStructMarshalJSON,
}
_classGoMap = &_objectClass{
goMapGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goMapDefineOwnProperty,
goMapDelete,
goMapEnumerate,
objectClone,
nil,
}
_classGoArray = &_objectClass{
goArrayGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goArrayDefineOwnProperty,
goArrayDelete,
goArrayEnumerate,
objectClone,
nil,
}
_classGoSlice = &_objectClass{
goSliceGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goSliceDefineOwnProperty,
goSliceDelete,
goSliceEnumerate,
objectClone,
nil,
}
}
// Allons-y
// 8.12.1
func objectGetOwnProperty(self *_object, name string) *_property {
// Return a _copy_ of the property
property, exists := self._read(name)
if !exists {
return nil
}
return &property
}
// 8.12.2
func objectGetProperty(self *_object, name string) *_property {
property := self.getOwnProperty(name)
if property != nil {
return property
}
if self.prototype != nil {
return self.prototype.getProperty(name)
}
return nil
}
// 8.12.3
func objectGet(self *_object, name string) Value {
property := self.getProperty(name)
if property != nil {
return property.get(self)
}
return Value{}
}
// 8.12.4
func objectCanPut(self *_object, name string) bool {
canPut, _, _ := _objectCanPut(self, name)
return canPut
}
func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) {
property = self.getOwnProperty(name)
if property != nil {
switch propertyValue := property.value.(type) {
case Value:
canPut = property.writable()
return
case _propertyGetSet:
setter = propertyValue[1]
canPut = setter != nil
return
default:
panic(self.runtime.panicTypeError())
}
}
if self.prototype == nil {
return self.extensible, nil, nil
}
property = self.prototype.getProperty(name)
if property == nil {
return self.extensible, nil, nil
}
switch propertyValue := property.value.(type) {
case Value:
if !self.extensible {
return false, nil, nil
}
return property.writable(), nil, nil
case _propertyGetSet:
setter = propertyValue[1]
canPut = setter != nil
return
default:
panic(self.runtime.panicTypeError())
}
}
// 8.12.5
func objectPut(self *_object, name string, value Value, throw bool) {
if true {
// Shortcut...
//
// So, right now, every class is using objectCanPut and every class
// is using objectPut.
//
// If that were to no longer be the case, we would have to have
// something to detect that here, so that we do not use an
// incompatible canPut routine
canPut, property, setter := _objectCanPut(self, name)
if !canPut {
self.runtime.typeErrorResult(throw)
} else if setter != nil {
setter.call(toValue(self), []Value{value}, false, nativeFrame)
} else if property != nil {
property.value = value
self.defineOwnProperty(name, *property, throw)
} else {
self.defineProperty(name, value, 0111, throw)
}
return
}
// The long way...
//
// Right now, code should never get here, see above
if !self.canPut(name) {
self.runtime.typeErrorResult(throw)
return
}
property := self.getOwnProperty(name)
if property == nil {
property = self.getProperty(name)
if property != nil {
if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor {
getSet[1].call(toValue(self), []Value{value}, false, nativeFrame)
return
}
}
self.defineProperty(name, value, 0111, throw)
} else {
switch propertyValue := property.value.(type) {
case Value:
property.value = value
self.defineOwnProperty(name, *property, throw)
case _propertyGetSet:
if propertyValue[1] != nil {
propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame)
return
}
if throw {
panic(self.runtime.panicTypeError())
}
default:
panic(self.runtime.panicTypeError())
}
}
}
// 8.12.6
func objectHasProperty(self *_object, name string) bool {
return self.getProperty(name) != nil
}
func objectHasOwnProperty(self *_object, name string) bool {
return self.getOwnProperty(name) != nil
}
// 8.12.9
func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
property, exists := self._read(name)
{
if !exists {
if !self.extensible {
goto Reject
}
if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
if newGetSet[0] == &_nilGetSetObject {
newGetSet[0] = nil
}
if newGetSet[1] == &_nilGetSetObject {
newGetSet[1] = nil
}
descriptor.value = newGetSet
}
self._write(name, descriptor.value, descriptor.mode)
return true
}
if descriptor.isEmpty() {
return true
}
// TODO Per 8.12.9.6 - We should shortcut here (returning true) if
// the current and new (define) properties are the same
configurable := property.configurable()
if !configurable {
if descriptor.configurable() {
goto Reject
}
// Test that, if enumerable is set on the property descriptor, then it should
// be the same as the existing property
if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() {
goto Reject
}
}
value, isDataDescriptor := property.value.(Value)
getSet, _ := property.value.(_propertyGetSet)
if descriptor.isGenericDescriptor() {
// GenericDescriptor
} else if isDataDescriptor != descriptor.isDataDescriptor() {
// DataDescriptor <=> AccessorDescriptor
if !configurable {
goto Reject
}
} else if isDataDescriptor && descriptor.isDataDescriptor() {
// DataDescriptor <=> DataDescriptor
if !configurable {
if !property.writable() && descriptor.writable() {
goto Reject
}
if !property.writable() {
if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) {
goto Reject
}
}
}
} else {
// AccessorDescriptor <=> AccessorDescriptor
newGetSet, _ := descriptor.value.(_propertyGetSet)
presentGet, presentSet := true, true
if newGetSet[0] == &_nilGetSetObject {
// Present, but nil
newGetSet[0] = nil
} else if newGetSet[0] == nil {
// Missing, not even nil
newGetSet[0] = getSet[0]
presentGet = false
}
if newGetSet[1] == &_nilGetSetObject {
// Present, but nil
newGetSet[1] = nil
} else if newGetSet[1] == nil {
// Missing, not even nil
newGetSet[1] = getSet[1]
presentSet = false
}
if !configurable {
if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) {
goto Reject
}
}
descriptor.value = newGetSet
}
{
// This section will preserve attributes of
// the original property, if necessary
value1 := descriptor.value
if value1 == nil {
value1 = property.value
} else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
if newGetSet[0] == &_nilGetSetObject {
newGetSet[0] = nil
}
if newGetSet[1] == &_nilGetSetObject {
newGetSet[1] = nil
}
value1 = newGetSet
}
mode1 := descriptor.mode
if mode1&0222 != 0 {
// TODO Factor this out into somewhere testable
// (Maybe put into switch ...)
mode0 := property.mode
if mode1&0200 != 0 {
if descriptor.isDataDescriptor() {
mode1 &= ^0200 // Turn off "writable" missing
mode1 |= (mode0 & 0100)
}
}
if mode1&020 != 0 {
mode1 |= (mode0 & 010)
}
if mode1&02 != 0 {
mode1 |= (mode0 & 01)
}
mode1 &= 0311 // 0311 to preserve the non-setting on "writable"
}
self._write(name, value1, mode1)
}
return true
}
Reject:
if throw {
panic(self.runtime.panicTypeError())
}
return false
}
func objectDelete(self *_object, name string, throw bool) bool {
property_ := self.getOwnProperty(name)
if property_ == nil {
return true
}
if property_.configurable() {
self._delete(name)
return true
}
return self.runtime.typeErrorResult(throw)
}
func objectClone(in *_object, out *_object, clone *_clone) *_object {
*out = *in
out.runtime = clone.runtime
if out.prototype != nil {
out.prototype = clone.object(in.prototype)
}
out.property = make(map[string]_property, len(in.property))
out.propertyOrder = make([]string, len(in.propertyOrder))
copy(out.propertyOrder, in.propertyOrder)
for index, property := range in.property {
out.property[index] = clone.property(property)
}
switch value := in.value.(type) {
case _nativeFunctionObject:
out.value = value
case _bindFunctionObject:
out.value = _bindFunctionObject{
target: clone.object(value.target),
this: clone.value(value.this),
argumentList: clone.valueArray(value.argumentList),
}
case _nodeFunctionObject:
out.value = _nodeFunctionObject{
node: value.node,
stash: clone.stash(value.stash),
}
case _argumentsObject:
out.value = value.clone(clone)
}
return out
}

View file

@ -1,770 +0,0 @@
/*
Package otto is a JavaScript parser and interpreter written natively in Go.
http://godoc.org/github.com/robertkrimen/otto
import (
"github.com/robertkrimen/otto"
)
Run something in the VM
vm := otto.New()
vm.Run(`
abc = 2 + 2;
console.log("The value of abc is " + abc); // 4
`)
Get a value out of the VM
value, err := vm.Get("abc")
value, _ := value.ToInteger()
}
Set a number
vm.Set("def", 11)
vm.Run(`
console.log("The value of def is " + def);
// The value of def is 11
`)
Set a string
vm.Set("xyzzy", "Nothing happens.")
vm.Run(`
console.log(xyzzy.length); // 16
`)
Get the value of an expression
value, _ = vm.Run("xyzzy.length")
{
// value is an int64 with a value of 16
value, _ := value.ToInteger()
}
An error happens
value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length")
if err != nil {
// err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
// If there is an error, then value.IsUndefined() is true
...
}
Set a Go function
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
return otto.Value{}
})
Set a Go function that returns something useful
vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
right, _ := call.Argument(0).ToInteger()
result, _ := vm.ToValue(2 + right)
return result
})
Use the functions in JavaScript
result, _ = vm.Run(`
sayHello("Xyzzy"); // Hello, Xyzzy.
sayHello(); // Hello, undefined
result = twoPlus(2.0); // 4
`)
Parser
A separate parser is available in the parser package if you're just interested in building an AST.
http://godoc.org/github.com/robertkrimen/otto/parser
Parse and return an AST
filename := "" // A filename is optional
src := `
// Sample xyzzy example
(function(){
if (3.14159 > 0) {
console.log("Hello, World.");
return;
}
var xyzzy = NaN;
console.log("Nothing happens.");
return xyzzy;
})();
`
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, filename, src, 0)
otto
You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto
$ go get -v github.com/robertkrimen/otto/otto
Run JavaScript by entering some source on stdin or by giving otto a filename:
$ otto example.js
underscore
Optionally include the JavaScript utility-belt library, underscore, with this import:
import (
"github.com/robertkrimen/otto"
_ "github.com/robertkrimen/otto/underscore"
)
// Now every otto runtime will come loaded with underscore
For more information: http://github.com/robertkrimen/otto/tree/master/underscore
Caveat Emptor
The following are some limitations with otto:
* "use strict" will parse, but does nothing.
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported.
Regular Expression Incompatibility
Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`.
Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax
Therefore, the following syntax is incompatible:
(?=) // Lookahead (positive), currently a parsing error
(?!) // Lookahead (backhead), currently a parsing error
\1 // Backreference (\1, \2, \3, ...), currently a parsing error
A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E
More information about re2: https://code.google.com/p/re2/
In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ].
The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
Halting Problem
If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this:
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/robertkrimen/otto"
)
var halt = errors.New("Stahp")
func main() {
runUnsafe(`var abc = [];`)
runUnsafe(`
while (true) {
// Loop forever
}`)
}
func runUnsafe(unsafe string) {
start := time.Now()
defer func() {
duration := time.Since(start)
if caught := recover(); caught != nil {
if caught == halt {
fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration)
return
}
panic(caught) // Something else happened, repanic!
}
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
}()
vm := otto.New()
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
go func() {
time.Sleep(2 * time.Second) // Stop after two seconds
vm.Interrupt <- func() {
panic(halt)
}
}()
vm.Run(unsafe) // Here be dragons (risky code)
}
Where is setTimeout/setInterval?
These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser).
It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case.
For an example of how this could be done in Go with otto, see natto:
http://github.com/robertkrimen/natto
Here is some more discussion of the issue:
* http://book.mixu.net/node/ch2.html
* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29
* http://aaroncrane.co.uk/2009/02/perl_safe_signals/
*/
package otto
import (
"fmt"
"strings"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/registry"
)
// Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace.
type Otto struct {
// Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example.
// See "Halting Problem" for more information.
Interrupt chan func()
runtime *_runtime
}
// New will allocate a new JavaScript runtime
func New() *Otto {
self := &Otto{
runtime: newContext(),
}
self.runtime.otto = self
self.runtime.traceLimit = 10
self.Set("console", self.runtime.newConsole())
registry.Apply(func(entry registry.Entry) {
self.Run(entry.Source())
})
return self
}
func (otto *Otto) clone() *Otto {
self := &Otto{
runtime: otto.runtime.clone(),
}
self.runtime.otto = self
return self
}
// Run will allocate a new JavaScript runtime, run the given source
// on the allocated runtime, and return the runtime, resulting value, and
// error (if any).
//
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
//
// src may also be a Script.
//
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
//
func Run(src interface{}) (*Otto, Value, error) {
otto := New()
value, err := otto.Run(src) // This already does safety checking
return otto, value, err
}
// Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any)
//
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
//
// If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing
// will be evaluated in this case).
//
// src may also be a Script.
//
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
//
func (self Otto) Run(src interface{}) (Value, error) {
value, err := self.runtime.cmpl_run(src, nil)
if !value.safe() {
value = Value{}
}
return value, err
}
// Eval will do the same thing as Run, except without leaving the current scope.
//
// By staying in the same scope, the code evaluated has access to everything
// already defined in the current stack frame. This is most useful in, for
// example, a debugger call.
func (self Otto) Eval(src interface{}) (Value, error) {
if self.runtime.scope == nil {
self.runtime.enterGlobalScope()
defer self.runtime.leaveScope()
}
value, err := self.runtime.cmpl_eval(src, nil)
if !value.safe() {
value = Value{}
}
return value, err
}
// Get the value of the top-level binding of the given name.
//
// If there is an error (like the binding does not exist), then the value
// will be undefined.
func (self Otto) Get(name string) (Value, error) {
value := Value{}
err := catchPanic(func() {
value = self.getValue(name)
})
if !value.safe() {
value = Value{}
}
return value, err
}
func (self Otto) getValue(name string) Value {
return self.runtime.globalStash.getBinding(name, false)
}
// Set the top-level binding of the given name to the given value.
//
// Set will automatically apply ToValue to the given value in order
// to convert it to a JavaScript value (type Value).
//
// If there is an error (like the binding is read-only, or the ToValue conversion
// fails), then an error is returned.
//
// If the top-level binding does not exist, it will be created.
func (self Otto) Set(name string, value interface{}) error {
{
value, err := self.ToValue(value)
if err != nil {
return err
}
err = catchPanic(func() {
self.setValue(name, value)
})
return err
}
}
func (self Otto) setValue(name string, value Value) {
self.runtime.globalStash.setValue(name, value, false)
}
func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) {
self.runtime.debugger = fn
}
func (self Otto) SetRandomSource(fn func() float64) {
self.runtime.random = fn
}
// SetStackDepthLimit sets an upper limit to the depth of the JavaScript
// stack. In simpler terms, this limits the number of "nested" function calls
// you can make in a particular interpreter instance.
//
// Note that this doesn't take into account the Go stack depth. If your
// JavaScript makes a call to a Go function, otto won't keep track of what
// happens outside the interpreter. So if your Go function is infinitely
// recursive, you're still in trouble.
func (self Otto) SetStackDepthLimit(limit int) {
self.runtime.stackLimit = limit
}
// SetStackTraceLimit sets an upper limit to the number of stack frames that
// otto will use when formatting an error's stack trace. By default, the limit
// is 10. This is consistent with V8 and SpiderMonkey.
//
// TODO: expose via `Error.stackTraceLimit`
func (self Otto) SetStackTraceLimit(limit int) {
self.runtime.traceLimit = limit
}
// MakeCustomError creates a new Error object with the given name and message,
// returning it as a Value.
func (self Otto) MakeCustomError(name, message string) Value {
return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0))
}
// MakeRangeError creates a new RangeError object with the given message,
// returning it as a Value.
func (self Otto) MakeRangeError(message string) Value {
return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message)))
}
// MakeSyntaxError creates a new SyntaxError object with the given message,
// returning it as a Value.
func (self Otto) MakeSyntaxError(message string) Value {
return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message)))
}
// MakeTypeError creates a new TypeError object with the given message,
// returning it as a Value.
func (self Otto) MakeTypeError(message string) Value {
return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message)))
}
// Context is a structure that contains information about the current execution
// context.
type Context struct {
Filename string
Line int
Column int
Callee string
Symbols map[string]Value
This Value
Stacktrace []string
}
// Context returns the current execution context of the vm, traversing up to
// ten stack frames, and skipping any innermost native function stack frames.
func (self Otto) Context() Context {
return self.ContextSkip(10, true)
}
// ContextLimit returns the current execution context of the vm, with a
// specific limit on the number of stack frames to traverse, skipping any
// innermost native function stack frames.
func (self Otto) ContextLimit(limit int) Context {
return self.ContextSkip(limit, true)
}
// ContextSkip returns the current execution context of the vm, with a
// specific limit on the number of stack frames to traverse, optionally
// skipping any innermost native function stack frames.
func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) {
// Ensure we are operating in a scope
if self.runtime.scope == nil {
self.runtime.enterGlobalScope()
defer self.runtime.leaveScope()
}
scope := self.runtime.scope
frame := scope.frame
for skipNative && frame.native && scope.outer != nil {
scope = scope.outer
frame = scope.frame
}
// Get location information
ctx.Filename = "<unknown>"
ctx.Callee = frame.callee
switch {
case frame.native:
ctx.Filename = frame.nativeFile
ctx.Line = frame.nativeLine
ctx.Column = 0
case frame.file != nil:
ctx.Filename = "<anonymous>"
if p := frame.file.Position(file.Idx(frame.offset)); p != nil {
ctx.Line = p.Line
ctx.Column = p.Column
if p.Filename != "" {
ctx.Filename = p.Filename
}
}
}
// Get the current scope this Value
ctx.This = toValue_object(scope.this)
// Build stacktrace (up to 10 levels deep)
ctx.Symbols = make(map[string]Value)
ctx.Stacktrace = append(ctx.Stacktrace, frame.location())
for limit != 0 {
// Get variables
stash := scope.lexical
for {
for _, name := range getStashProperties(stash) {
if _, ok := ctx.Symbols[name]; !ok {
ctx.Symbols[name] = stash.getBinding(name, true)
}
}
stash = stash.outer()
if stash == nil || stash.outer() == nil {
break
}
}
scope = scope.outer
if scope == nil {
break
}
if scope.frame.offset >= 0 {
ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location())
}
limit--
}
return
}
// Call the given JavaScript with a given this and arguments.
//
// If this is nil, then some special handling takes place to determine the proper
// this value, falling back to a "standard" invocation if necessary (where this is
// undefined).
//
// If source begins with "new " (A lowercase new followed by a space), then
// Call will invoke the function constructor rather than performing a function call.
// In this case, the this argument has no effect.
//
// // value is a String object
// value, _ := vm.Call("Object", nil, "Hello, World.")
//
// // Likewise...
// value, _ := vm.Call("new Object", nil, "Hello, World.")
//
// // This will perform a concat on the given array and return the result
// // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ]
// value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc")
//
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) {
thisValue := Value{}
construct := false
if strings.HasPrefix(source, "new ") {
source = source[4:]
construct = true
}
// FIXME enterGlobalScope
self.runtime.enterGlobalScope()
defer func() {
self.runtime.leaveScope()
}()
if !construct && this == nil {
program, err := self.runtime.cmpl_parse("", source+"()", nil)
if err == nil {
if node, ok := program.body[0].(*_nodeExpressionStatement); ok {
if node, ok := node.expression.(*_nodeCallExpression); ok {
var value Value
err := catchPanic(func() {
value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList)
})
if err != nil {
return Value{}, err
}
return value, nil
}
}
}
} else {
value, err := self.ToValue(this)
if err != nil {
return Value{}, err
}
thisValue = value
}
{
this := thisValue
fn, err := self.Run(source)
if err != nil {
return Value{}, err
}
if construct {
result, err := fn.constructSafe(self.runtime, this, argumentList...)
if err != nil {
return Value{}, err
}
return result, nil
}
result, err := fn.Call(this, argumentList...)
if err != nil {
return Value{}, err
}
return result, nil
}
}
// Object will run the given source and return the result as an object.
//
// For example, accessing an existing object:
//
// object, _ := vm.Object(`Number`)
//
// Or, creating a new object:
//
// object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`)
//
// Or, creating and assigning an object:
//
// object, _ := vm.Object(`xyzzy = {}`)
// object.Set("volume", 11)
//
// If there is an error (like the source does not result in an object), then
// nil and an error is returned.
func (self Otto) Object(source string) (*Object, error) {
value, err := self.runtime.cmpl_run(source, nil)
if err != nil {
return nil, err
}
if value.IsObject() {
return value.Object(), nil
}
return nil, fmt.Errorf("value is not an object")
}
// ToValue will convert an interface{} value to a value digestible by otto/JavaScript.
func (self Otto) ToValue(value interface{}) (Value, error) {
return self.runtime.safeToValue(value)
}
// Copy will create a copy/clone of the runtime.
//
// Copy is useful for saving some time when creating many similar runtimes.
//
// This method works by walking the original runtime and cloning each object, scope, stash,
// etc. into a new runtime.
//
// Be on the lookout for memory leaks or inadvertent sharing of resources.
func (in *Otto) Copy() *Otto {
out := &Otto{
runtime: in.runtime.clone(),
}
out.runtime.otto = out
return out
}
// Object{}
// Object is the representation of a JavaScript object.
type Object struct {
object *_object
value Value
}
func _newObject(object *_object, value Value) *Object {
// value MUST contain object!
return &Object{
object: object,
value: value,
}
}
// Call a method on the object.
//
// It is essentially equivalent to:
//
// var method, _ := object.Get(name)
// method.Call(object, argumentList...)
//
// An undefined value and an error will result if:
//
// 1. There is an error during conversion of the argument list
// 2. The property is not actually a function
// 3. An (uncaught) exception is thrown
//
func (self Object) Call(name string, argumentList ...interface{}) (Value, error) {
// TODO: Insert an example using JavaScript below...
// e.g., Object("JSON").Call("stringify", ...)
function, err := self.Get(name)
if err != nil {
return Value{}, err
}
return function.Call(self.Value(), argumentList...)
}
// Value will return self as a value.
func (self Object) Value() Value {
return self.value
}
// Get the value of the property with the given name.
func (self Object) Get(name string) (Value, error) {
value := Value{}
err := catchPanic(func() {
value = self.object.get(name)
})
if !value.safe() {
value = Value{}
}
return value, err
}
// Set the property of the given name to the given value.
//
// An error will result if the setting the property triggers an exception (i.e. read-only),
// or there is an error during conversion of the given value.
func (self Object) Set(name string, value interface{}) error {
{
value, err := self.object.runtime.safeToValue(value)
if err != nil {
return err
}
err = catchPanic(func() {
self.object.put(name, value, true)
})
return err
}
}
// Keys gets the keys for the given object.
//
// Equivalent to calling Object.keys on the object.
func (self Object) Keys() []string {
var keys []string
self.object.enumerate(false, func(name string) bool {
keys = append(keys, name)
return true
})
return keys
}
// KeysByParent gets the keys (and those of the parents) for the given object,
// in order of "closest" to "furthest".
func (self Object) KeysByParent() [][]string {
var a [][]string
for o := self.object; o != nil; o = o.prototype {
var l []string
o.enumerate(false, func(name string) bool {
l = append(l, name)
return true
})
a = append(a, l)
}
return a
}
// Class will return the class string of the object.
//
// The return value will (generally) be one of:
//
// Object
// Function
// Array
// String
// Number
// Boolean
// Date
// RegExp
//
func (self Object) Class() string {
return self.object.class
}

View file

@ -1,178 +0,0 @@
package otto
import (
"fmt"
"regexp"
runtime_ "runtime"
"strconv"
"strings"
)
var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`)
func isIdentifier(string_ string) bool {
return isIdentifier_Regexp.MatchString(string_)
}
func (self *_runtime) toValueArray(arguments ...interface{}) []Value {
length := len(arguments)
if length == 1 {
if valueArray, ok := arguments[0].([]Value); ok {
return valueArray
}
return []Value{self.toValue(arguments[0])}
}
valueArray := make([]Value, length)
for index, value := range arguments {
valueArray[index] = self.toValue(value)
}
return valueArray
}
func stringToArrayIndex(name string) int64 {
index, err := strconv.ParseInt(name, 10, 64)
if err != nil {
return -1
}
if index < 0 {
return -1
}
if index >= maxUint32 {
// The value 2^32 (or above) is not a valid index because
// you cannot store a uint32 length for an index of uint32
return -1
}
return index
}
func isUint32(value int64) bool {
return value >= 0 && value <= maxUint32
}
func arrayIndexToString(index int64) string {
return strconv.FormatInt(index, 10)
}
func valueOfArrayIndex(array []Value, index int) Value {
value, _ := getValueOfArrayIndex(array, index)
return value
}
func getValueOfArrayIndex(array []Value, index int) (Value, bool) {
if index >= 0 && index < len(array) {
value := array[index]
if !value.isEmpty() {
return value, true
}
}
return Value{}, false
}
// A range index can be anything from 0 up to length. It is NOT safe to use as an index
// to an array, but is useful for slicing and in some ECMA algorithms.
func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 {
index := indexValue.number().int64
if negativeIsZero {
if index < 0 {
index = 0
}
// minimum(index, length)
if index >= length {
index = length
}
return index
}
if index < 0 {
index += length
if index < 0 {
index = 0
}
} else {
if index > length {
index = length
}
}
return index
}
func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) {
start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero)
if len(array) == 1 {
// If there is only the start argument, then end = size
end = size
return
}
// Assuming the argument is undefined...
end = size
endValue := valueOfArrayIndex(array, 1)
if !endValue.IsUndefined() {
// Which it is not, so get the value as an array index
end = valueToRangeIndex(endValue, size, negativeIsZero)
}
return
}
func rangeStartLength(source []Value, size int64) (start, length int64) {
start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false)
// Assume the second argument is missing or undefined
length = int64(size)
if len(source) == 1 {
// If there is only the start argument, then length = size
return
}
lengthValue := valueOfArrayIndex(source, 1)
if !lengthValue.IsUndefined() {
// Which it is not, so get the value as an array index
length = lengthValue.number().int64
}
return
}
func boolFields(input string) (result map[string]bool) {
result = map[string]bool{}
for _, word := range strings.Fields(input) {
result[word] = true
}
return result
}
func hereBeDragons(arguments ...interface{}) string {
pc, _, _, _ := runtime_.Caller(1)
name := runtime_.FuncForPC(pc).Name()
message := fmt.Sprintf("Here be dragons -- %s", name)
if len(arguments) > 0 {
message += ": "
argument0 := fmt.Sprintf("%s", arguments[0])
if len(arguments) == 1 {
message += argument0
} else {
message += fmt.Sprintf(argument0, arguments[1:]...)
}
} else {
message += "."
}
return message
}
func throwHereBeDragons(arguments ...interface{}) {
panic(hereBeDragons(arguments...))
}
func eachPair(list []interface{}, fn func(_0, _1 interface{})) {
for len(list) > 0 {
var _0, _1 interface{}
_0 = list[0]
list = list[1:] // Pop off first
if len(list) > 0 {
_1 = list[0]
list = list[1:] // Pop off second
}
fn(_0, _1)
}
}

View file

@ -1,220 +0,0 @@
package otto
// property
type _propertyMode int
const (
modeWriteMask _propertyMode = 0700
modeEnumerateMask = 0070
modeConfigureMask = 0007
modeOnMask = 0111
modeOffMask = 0000
modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off"
)
type _propertyGetSet [2]*_object
var _nilGetSetObject _object = _object{}
type _property struct {
value interface{}
mode _propertyMode
}
func (self _property) writable() bool {
return self.mode&modeWriteMask == modeWriteMask&modeOnMask
}
func (self *_property) writeOn() {
self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeOnMask)
}
func (self *_property) writeOff() {
self.mode &= ^modeWriteMask
}
func (self *_property) writeClear() {
self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeSetMask)
}
func (self _property) writeSet() bool {
return 0 == self.mode&modeWriteMask&modeSetMask
}
func (self _property) enumerable() bool {
return self.mode&modeEnumerateMask == modeEnumerateMask&modeOnMask
}
func (self *_property) enumerateOn() {
self.mode = (self.mode & ^modeEnumerateMask) | (modeEnumerateMask & modeOnMask)
}
func (self *_property) enumerateOff() {
self.mode &= ^modeEnumerateMask
}
func (self _property) enumerateSet() bool {
return 0 == self.mode&modeEnumerateMask&modeSetMask
}
func (self _property) configurable() bool {
return self.mode&modeConfigureMask == modeConfigureMask&modeOnMask
}
func (self *_property) configureOn() {
self.mode = (self.mode & ^modeConfigureMask) | (modeConfigureMask & modeOnMask)
}
func (self *_property) configureOff() {
self.mode &= ^modeConfigureMask
}
func (self _property) configureSet() bool {
return 0 == self.mode&modeConfigureMask&modeSetMask
}
func (self _property) copy() *_property {
property := self
return &property
}
func (self _property) get(this *_object) Value {
switch value := self.value.(type) {
case Value:
return value
case _propertyGetSet:
if value[0] != nil {
return value[0].call(toValue(this), nil, false, nativeFrame)
}
}
return Value{}
}
func (self _property) isAccessorDescriptor() bool {
setGet, test := self.value.(_propertyGetSet)
return test && (setGet[0] != nil || setGet[1] != nil)
}
func (self _property) isDataDescriptor() bool {
if self.writeSet() { // Either "On" or "Off"
return true
}
value, valid := self.value.(Value)
return valid && !value.isEmpty()
}
func (self _property) isGenericDescriptor() bool {
return !(self.isDataDescriptor() || self.isAccessorDescriptor())
}
func (self _property) isEmpty() bool {
return self.mode == 0222 && self.isGenericDescriptor()
}
// _enumerableValue, _enumerableTrue, _enumerableFalse?
// .enumerableValue() .enumerableExists()
func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) {
objectDescriptor := value._object()
if objectDescriptor == nil {
panic(rt.panicTypeError())
}
{
descriptor.mode = modeSetMask // Initially nothing is set
if objectDescriptor.hasProperty("enumerable") {
if objectDescriptor.get("enumerable").bool() {
descriptor.enumerateOn()
} else {
descriptor.enumerateOff()
}
}
if objectDescriptor.hasProperty("configurable") {
if objectDescriptor.get("configurable").bool() {
descriptor.configureOn()
} else {
descriptor.configureOff()
}
}
if objectDescriptor.hasProperty("writable") {
if objectDescriptor.get("writable").bool() {
descriptor.writeOn()
} else {
descriptor.writeOff()
}
}
}
var getter, setter *_object
getterSetter := false
if objectDescriptor.hasProperty("get") {
value := objectDescriptor.get("get")
if value.IsDefined() {
if !value.isCallable() {
panic(rt.panicTypeError())
}
getter = value._object()
getterSetter = true
} else {
getter = &_nilGetSetObject
getterSetter = true
}
}
if objectDescriptor.hasProperty("set") {
value := objectDescriptor.get("set")
if value.IsDefined() {
if !value.isCallable() {
panic(rt.panicTypeError())
}
setter = value._object()
getterSetter = true
} else {
setter = &_nilGetSetObject
getterSetter = true
}
}
if getterSetter {
if descriptor.writeSet() {
panic(rt.panicTypeError())
}
descriptor.value = _propertyGetSet{getter, setter}
}
if objectDescriptor.hasProperty("value") {
if getterSetter {
panic(rt.panicTypeError())
}
descriptor.value = objectDescriptor.get("value")
}
return
}
func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object {
object := self.newObject()
if descriptor.isDataDescriptor() {
object.defineProperty("value", descriptor.value.(Value), 0111, false)
object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false)
} else if descriptor.isAccessorDescriptor() {
getSet := descriptor.value.(_propertyGetSet)
get := Value{}
if getSet[0] != nil {
get = toValue_object(getSet[0])
}
set := Value{}
if getSet[1] != nil {
set = toValue_object(getSet[1])
}
object.defineProperty("get", get, 0111, false)
object.defineProperty("set", set, 0111, false)
}
object.defineProperty("enumerable", toValue_bool(descriptor.enumerable()), 0111, false)
object.defineProperty("configurable", toValue_bool(descriptor.configurable()), 0111, false)
return object
}

View file

@ -1,30 +0,0 @@
package otto
import ()
type _resultKind int
const (
resultNormal _resultKind = iota
resultReturn
resultBreak
resultContinue
)
type _result struct {
kind _resultKind
value Value
target string
}
func newReturnResult(value Value) _result {
return _result{resultReturn, value, ""}
}
func newContinueResult(target string) _result {
return _result{resultContinue, emptyValue, target}
}
func newBreakResult(target string) _result {
return _result{resultBreak, emptyValue, target}
}

View file

@ -1,711 +0,0 @@
package otto
import (
"errors"
"fmt"
"math"
"path"
"reflect"
"runtime"
"strconv"
"sync"
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/parser"
)
type _global struct {
Object *_object // Object( ... ), new Object( ... ) - 1 (length)
Function *_object // Function( ... ), new Function( ... ) - 1
Array *_object // Array( ... ), new Array( ... ) - 1
String *_object // String( ... ), new String( ... ) - 1
Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1
Number *_object // Number( ... ), new Number( ... ) - 1
Math *_object
Date *_object // Date( ... ), new Date( ... ) - 7
RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2
Error *_object // Error( ... ), new Error( ... ) - 1
EvalError *_object
TypeError *_object
RangeError *_object
ReferenceError *_object
SyntaxError *_object
URIError *_object
JSON *_object
ObjectPrototype *_object // Object.prototype
FunctionPrototype *_object // Function.prototype
ArrayPrototype *_object // Array.prototype
StringPrototype *_object // String.prototype
BooleanPrototype *_object // Boolean.prototype
NumberPrototype *_object // Number.prototype
DatePrototype *_object // Date.prototype
RegExpPrototype *_object // RegExp.prototype
ErrorPrototype *_object // Error.prototype
EvalErrorPrototype *_object
TypeErrorPrototype *_object
RangeErrorPrototype *_object
ReferenceErrorPrototype *_object
SyntaxErrorPrototype *_object
URIErrorPrototype *_object
}
type _runtime struct {
global _global
globalObject *_object
globalStash *_objectStash
scope *_scope
otto *Otto
eval *_object // The builtin eval, for determine indirect versus direct invocation
debugger func(*Otto)
random func() float64
stackLimit int
traceLimit int
labels []string // FIXME
lck sync.Mutex
}
func (self *_runtime) enterScope(scope *_scope) {
scope.outer = self.scope
if self.scope != nil {
if self.stackLimit != 0 && self.scope.depth+1 >= self.stackLimit {
panic(self.panicRangeError("Maximum call stack size exceeded"))
}
scope.depth = self.scope.depth + 1
}
self.scope = scope
}
func (self *_runtime) leaveScope() {
self.scope = self.scope.outer
}
// FIXME This is used in two places (cloning)
func (self *_runtime) enterGlobalScope() {
self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject))
}
func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash {
if outer == nil {
outer = self.globalStash
}
stash := self.newFunctionStash(outer)
var thisObject *_object
switch this.kind {
case valueUndefined, valueNull:
thisObject = self.globalObject
default:
thisObject = self.toObject(this)
}
self.enterScope(newScope(stash, stash, thisObject))
return stash
}
func (self *_runtime) putValue(reference _reference, value Value) {
name := reference.putValue(value)
if name != "" {
// Why? -- If reference.base == nil
// strict = false
self.globalObject.defineProperty(name, value, 0111, false)
}
}
func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) {
// resultValue = The value of the block (e.g. the last statement)
// throw = Something was thrown
// throwValue = The value of what was thrown
// other = Something that changes flow (return, break, continue) that is not a throw
// Otherwise, some sort of unknown panic happened, we'll just propagate it
defer func() {
if caught := recover(); caught != nil {
if exception, ok := caught.(*_exception); ok {
caught = exception.eject()
}
switch caught := caught.(type) {
case _error:
exception = true
tryValue = toValue_object(self.newError(caught.name, caught.messageValue(), 0))
case Value:
exception = true
tryValue = caught
default:
panic(caught)
}
}
}()
tryValue = inner()
return
}
// toObject
func (self *_runtime) toObject(value Value) *_object {
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
panic(self.panicTypeError())
case valueBoolean:
return self.newBoolean(value)
case valueString:
return self.newString(value)
case valueNumber:
return self.newNumber(value)
case valueObject:
return value._object()
}
panic(self.panicTypeError())
}
func (self *_runtime) objectCoerce(value Value) (*_object, error) {
switch value.kind {
case valueUndefined:
return nil, errors.New("undefined")
case valueNull:
return nil, errors.New("null")
case valueBoolean:
return self.newBoolean(value), nil
case valueString:
return self.newString(value), nil
case valueNumber:
return self.newNumber(value), nil
case valueObject:
return value._object(), nil
}
panic(self.panicTypeError())
}
func checkObjectCoercible(rt *_runtime, value Value) {
isObject, mustCoerce := testObjectCoercible(value)
if !isObject && !mustCoerce {
panic(rt.panicTypeError())
}
}
// testObjectCoercible
func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
switch value.kind {
case valueReference, valueEmpty, valueNull, valueUndefined:
return false, false
case valueNumber, valueString, valueBoolean:
return false, true
case valueObject:
return true, false
default:
panic("this should never happen")
}
}
func (self *_runtime) safeToValue(value interface{}) (Value, error) {
result := Value{}
err := catchPanic(func() {
result = self.toValue(value)
})
return result, err
}
// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics.
// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily.
func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value {
val := reflect.ValueOf(v.export())
if val.Kind() == t.Kind() {
return val
}
if val.Kind() == reflect.Interface {
val = reflect.ValueOf(val.Interface())
}
switch val.Kind() {
case reflect.Float32, reflect.Float64:
f64 := val.Float()
switch t.Kind() {
case reflect.Float64:
return reflect.ValueOf(f64)
case reflect.Float32:
if reflect.Zero(t).OverflowFloat(f64) {
panic(self.panicRangeError("converting float64 to float32 would overflow"))
}
return val.Convert(t)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i64 := int64(f64)
if float64(i64) != f64 {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t)))
}
// The float represents an integer
val = reflect.ValueOf(i64)
default:
panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t)))
}
}
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i64 := val.Int()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if reflect.Zero(t).OverflowInt(i64) {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
}
return val.Convert(t)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if i64 < 0 {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t)))
}
if reflect.Zero(t).OverflowUint(uint64(i64)) {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
}
return val.Convert(t)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
u64 := val.Uint()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
}
return val.Convert(t)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if reflect.Zero(t).OverflowUint(u64) {
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
}
return val.Convert(t)
}
}
panic(self.panicTypeError(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type())))
}
var typeOfValue = reflect.TypeOf(Value{})
// convertCallParameter converts request val to type t if possible.
// If the conversion fails due to overflow or type miss-match then it panics.
// If no conversion is known then the original value is returned.
func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value {
if t == typeOfValue {
return reflect.ValueOf(v)
}
if v.kind == valueObject {
if gso, ok := v._object().value.(*_goStructObject); ok {
if gso.value.Type().AssignableTo(t) {
return gso.value
}
}
}
if t.Kind() == reflect.Interface {
iv := reflect.ValueOf(v.export())
if iv.Type().AssignableTo(t) {
return iv
}
}
tk := t.Kind()
if tk == reflect.Ptr {
switch v.kind {
case valueEmpty, valueNull, valueUndefined:
return reflect.Zero(t)
default:
var vv reflect.Value
if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil {
if vv.CanAddr() {
return vv.Addr()
}
pv := reflect.New(vv.Type())
pv.Elem().Set(vv)
return pv
}
}
}
switch tk {
case reflect.Bool:
return reflect.ValueOf(v.bool())
case reflect.String:
switch v.kind {
case valueString:
return reflect.ValueOf(v.value)
case valueNumber:
return reflect.ValueOf(fmt.Sprintf("%v", v.value))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
switch v.kind {
case valueNumber:
return self.convertNumeric(v, t)
}
case reflect.Slice:
if o := v._object(); o != nil {
if lv := o.get("length"); lv.IsNumber() {
l := lv.number().int64
s := reflect.MakeSlice(t, int(l), int(l))
tt := t.Elem()
if o.class == "Array" {
for i := int64(0); i < l; i++ {
p, ok := o.property[strconv.FormatInt(i, 10)]
if !ok {
continue
}
e, ok := p.value.(Value)
if !ok {
continue
}
ev := self.convertCallParameter(e, tt)
s.Index(int(i)).Set(ev)
}
} else if o.class == "GoArray" {
var gslice bool
switch o.value.(type) {
case *_goSliceObject:
gslice = true
case *_goArrayObject:
gslice = false
}
for i := int64(0); i < l; i++ {
var p *_property
if gslice {
p = goSliceGetOwnProperty(o, strconv.FormatInt(i, 10))
} else {
p = goArrayGetOwnProperty(o, strconv.FormatInt(i, 10))
}
if p == nil {
continue
}
e, ok := p.value.(Value)
if !ok {
continue
}
ev := self.convertCallParameter(e, tt)
s.Index(int(i)).Set(ev)
}
}
return s
}
}
case reflect.Map:
if o := v._object(); o != nil && t.Key().Kind() == reflect.String {
m := reflect.MakeMap(t)
o.enumerate(false, func(k string) bool {
m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem()))
return true
})
return m
}
case reflect.Func:
if t.NumOut() > 1 {
panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported"))
}
if o := v._object(); o != nil && o.class == "Function" {
return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
l := make([]interface{}, len(args))
for i, a := range args {
if a.CanInterface() {
l[i] = a.Interface()
}
}
rv, err := v.Call(nullValue, l...)
if err != nil {
panic(err)
}
if t.NumOut() == 0 {
return nil
}
return []reflect.Value{self.convertCallParameter(rv, t.Out(0))}
})
}
}
if tk == reflect.String {
if o := v._object(); o != nil && o.hasProperty("toString") {
if fn := o.get("toString"); fn.IsFunction() {
sv, err := fn.Call(v)
if err != nil {
panic(err)
}
var r reflect.Value
if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil {
return r
}
}
}
return reflect.ValueOf(v.String())
}
s := "OTTO DOES NOT UNDERSTAND THIS TYPE"
switch v.kind {
case valueBoolean:
s = "boolean"
case valueNull:
s = "null"
case valueNumber:
s = "number"
case valueString:
s = "string"
case valueUndefined:
s = "undefined"
case valueObject:
s = v.Class()
}
panic(self.panicTypeError("can't convert from %q to %q", s, t.String()))
}
func (self *_runtime) toValue(value interface{}) Value {
switch value := value.(type) {
case Value:
return value
case func(FunctionCall) Value:
var name, file string
var line int
pc := reflect.ValueOf(value).Pointer()
fn := runtime.FuncForPC(pc)
if fn != nil {
name = fn.Name()
file, line = fn.FileLine(pc)
file = path.Base(file)
}
return toValue_object(self.newNativeFunction(name, file, line, value))
case _nativeFunction:
var name, file string
var line int
pc := reflect.ValueOf(value).Pointer()
fn := runtime.FuncForPC(pc)
if fn != nil {
name = fn.Name()
file, line = fn.FileLine(pc)
file = path.Base(file)
}
return toValue_object(self.newNativeFunction(name, file, line, value))
case Object, *Object, _object, *_object:
// Nothing happens.
// FIXME We should really figure out what can come here.
// This catch-all is ugly.
default:
{
value := reflect.ValueOf(value)
switch value.Kind() {
case reflect.Ptr:
switch reflect.Indirect(value).Kind() {
case reflect.Struct:
return toValue_object(self.newGoStructObject(value))
case reflect.Array:
return toValue_object(self.newGoArray(value))
}
case reflect.Struct:
return toValue_object(self.newGoStructObject(value))
case reflect.Map:
return toValue_object(self.newGoMapObject(value))
case reflect.Slice:
return toValue_object(self.newGoSlice(value))
case reflect.Array:
return toValue_object(self.newGoArray(value))
case reflect.Func:
var name, file string
var line int
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr {
pc := v.Pointer()
fn := runtime.FuncForPC(pc)
if fn != nil {
name = fn.Name()
file, line = fn.FileLine(pc)
file = path.Base(file)
}
}
typ := value.Type()
return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value {
nargs := typ.NumIn()
if len(c.ArgumentList) != nargs {
if typ.IsVariadic() {
if len(c.ArgumentList) < nargs-1 {
panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList))))
}
} else {
panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList))))
}
}
in := make([]reflect.Value, len(c.ArgumentList))
callSlice := false
for i, a := range c.ArgumentList {
var t reflect.Type
n := i
if n >= nargs-1 && typ.IsVariadic() {
if n > nargs-1 {
n = nargs - 1
}
t = typ.In(n).Elem()
} else {
t = typ.In(n)
}
// if this is a variadic Go function, and the caller has supplied
// exactly the number of JavaScript arguments required, and this
// is the last JavaScript argument, try treating the it as the
// actual set of variadic Go arguments. if that succeeds, break
// out of the loop.
if typ.IsVariadic() && len(c.ArgumentList) == nargs && i == nargs-1 {
var v reflect.Value
if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil {
in[i] = v
callSlice = true
break
}
}
in[i] = self.convertCallParameter(a, t)
}
var out []reflect.Value
if callSlice {
out = value.CallSlice(in)
} else {
out = value.Call(in)
}
switch len(out) {
case 0:
return Value{}
case 1:
return self.toValue(out[0].Interface())
default:
s := make([]interface{}, len(out))
for i, v := range out {
s[i] = self.toValue(v.Interface())
}
return self.toValue(s)
}
}))
}
}
}
return toValue(value)
}
func (runtime *_runtime) newGoSlice(value reflect.Value) *_object {
self := runtime.newGoSliceObject(value)
self.prototype = runtime.global.ArrayPrototype
return self
}
func (runtime *_runtime) newGoArray(value reflect.Value) *_object {
self := runtime.newGoArrayObject(value)
self.prototype = runtime.global.ArrayPrototype
return self
}
func (runtime *_runtime) parse(filename string, src, sm interface{}) (*ast.Program, error) {
return parser.ParseFileWithSourceMap(nil, filename, src, sm, 0)
}
func (runtime *_runtime) cmpl_parse(filename string, src, sm interface{}) (*_nodeProgram, error) {
program, err := parser.ParseFileWithSourceMap(nil, filename, src, sm, 0)
if err != nil {
return nil, err
}
return cmpl_parse(program), nil
}
func (self *_runtime) parseSource(src, sm interface{}) (*_nodeProgram, *ast.Program, error) {
switch src := src.(type) {
case *ast.Program:
return nil, src, nil
case *Script:
return src.program, nil, nil
}
program, err := self.parse("", src, sm)
return nil, program, err
}
func (self *_runtime) cmpl_runOrEval(src, sm interface{}, eval bool) (Value, error) {
result := Value{}
cmpl_program, program, err := self.parseSource(src, sm)
if err != nil {
return result, err
}
if cmpl_program == nil {
cmpl_program = cmpl_parse(program)
}
err = catchPanic(func() {
result = self.cmpl_evaluate_nodeProgram(cmpl_program, eval)
})
switch result.kind {
case valueEmpty:
result = Value{}
case valueReference:
result = result.resolve()
}
return result, err
}
func (self *_runtime) cmpl_run(src, sm interface{}) (Value, error) {
return self.cmpl_runOrEval(src, sm, false)
}
func (self *_runtime) cmpl_eval(src, sm interface{}) (Value, error) {
return self.cmpl_runOrEval(src, sm, true)
}
func (self *_runtime) parseThrow(err error) {
if err == nil {
return
}
switch err := err.(type) {
case parser.ErrorList:
{
err := err[0]
if err.Message == "Invalid left-hand side in assignment" {
panic(self.panicReferenceError(err.Message))
}
panic(self.panicSyntaxError(err.Message))
}
}
panic(self.panicSyntaxError(err.Error()))
}
func (self *_runtime) cmpl_parseOrThrow(src, sm interface{}) *_nodeProgram {
program, err := self.cmpl_parse("", src, sm)
self.parseThrow(err) // Will panic/throw appropriately
return program
}

View file

@ -1,35 +0,0 @@
package otto
// _scope:
// entryFile
// entryIdx
// top?
// outer => nil
// _stash:
// lexical
// variable
//
// _thisStash (ObjectEnvironment)
// _fnStash
// _dclStash
// An ECMA-262 ExecutionContext
type _scope struct {
lexical _stash
variable _stash
this *_object
eval bool // Replace this with kind?
outer *_scope
depth int
frame _frame
}
func newScope(lexical _stash, variable _stash, this *_object) *_scope {
return &_scope{
lexical: lexical,
variable: variable,
this: this,
}
}

View file

@ -1,119 +0,0 @@
package otto
import (
"bytes"
"encoding/gob"
"errors"
)
var ErrVersion = errors.New("version mismatch")
var scriptVersion = "2014-04-13/1"
// Script is a handle for some (reusable) JavaScript.
// Passing a Script value to a run method will evaluate the JavaScript.
//
type Script struct {
version string
program *_nodeProgram
filename string
src string
}
// Compile will parse the given source and return a Script value or nil and
// an error if there was a problem during compilation.
//
// script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
// vm.Run(script)
//
func (self *Otto) Compile(filename string, src interface{}) (*Script, error) {
return self.CompileWithSourceMap(filename, src, nil)
}
// CompileWithSourceMap does the same thing as Compile, but with the obvious
// difference of applying a source map.
func (self *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) {
program, err := self.runtime.parse(filename, src, sm)
if err != nil {
return nil, err
}
cmpl_program := cmpl_parse(program)
script := &Script{
version: scriptVersion,
program: cmpl_program,
filename: filename,
src: program.File.Source(),
}
return script, nil
}
func (self *Script) String() string {
return "// " + self.filename + "\n" + self.src
}
// MarshalBinary will marshal a script into a binary form. A marshalled script
// that is later unmarshalled can be executed on the same version of the otto runtime.
//
// The binary format can change at any time and should be considered unspecified and opaque.
//
func (self *Script) marshalBinary() ([]byte, error) {
var bfr bytes.Buffer
encoder := gob.NewEncoder(&bfr)
err := encoder.Encode(self.version)
if err != nil {
return nil, err
}
err = encoder.Encode(self.program)
if err != nil {
return nil, err
}
err = encoder.Encode(self.filename)
if err != nil {
return nil, err
}
err = encoder.Encode(self.src)
if err != nil {
return nil, err
}
return bfr.Bytes(), nil
}
// UnmarshalBinary will vivify a marshalled script into something usable. If the script was
// originally marshalled on a different version of the otto runtime, then this method
// will return an error.
//
// The binary format can change at any time and should be considered unspecified and opaque.
//
func (self *Script) unmarshalBinary(data []byte) error {
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&self.version)
if err != nil {
goto error
}
if self.version != scriptVersion {
err = ErrVersion
goto error
}
err = decoder.Decode(&self.program)
if err != nil {
goto error
}
err = decoder.Decode(&self.filename)
if err != nil {
goto error
}
err = decoder.Decode(&self.src)
if err != nil {
goto error
}
return nil
error:
self.version = ""
self.program = nil
self.filename = ""
self.src = ""
return err
}

View file

@ -1,296 +0,0 @@
package otto
import (
"fmt"
)
// ======
// _stash
// ======
type _stash interface {
hasBinding(string) bool //
createBinding(string, bool, Value) // CreateMutableBinding
setBinding(string, Value, bool) // SetMutableBinding
getBinding(string, bool) Value // GetBindingValue
deleteBinding(string) bool //
setValue(string, Value, bool) // createBinding + setBinding
outer() _stash
runtime() *_runtime
newReference(string, bool, _at) _reference
clone(clone *_clone) _stash
}
// ==========
// _objectStash
// ==========
type _objectStash struct {
_runtime *_runtime
_outer _stash
object *_object
}
func (self *_objectStash) runtime() *_runtime {
return self._runtime
}
func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash {
if object == nil {
object = runtime.newBaseObject()
object.class = "environment"
}
return &_objectStash{
_runtime: runtime,
_outer: outer,
object: object,
}
}
func (in *_objectStash) clone(clone *_clone) _stash {
out, exists := clone.objectStash(in)
if exists {
return out
}
*out = _objectStash{
clone.runtime,
clone.stash(in._outer),
clone.object(in.object),
}
return out
}
func (self *_objectStash) hasBinding(name string) bool {
return self.object.hasProperty(name)
}
func (self *_objectStash) createBinding(name string, deletable bool, value Value) {
if self.object.hasProperty(name) {
panic(hereBeDragons())
}
mode := _propertyMode(0111)
if !deletable {
mode = _propertyMode(0110)
}
// TODO False?
self.object.defineProperty(name, value, mode, false)
}
func (self *_objectStash) setBinding(name string, value Value, strict bool) {
self.object.put(name, value, strict)
}
func (self *_objectStash) setValue(name string, value Value, throw bool) {
if !self.hasBinding(name) {
self.createBinding(name, true, value) // Configurable by default
} else {
self.setBinding(name, value, throw)
}
}
func (self *_objectStash) getBinding(name string, throw bool) Value {
if self.object.hasProperty(name) {
return self.object.get(name)
}
if throw { // strict?
panic(self._runtime.panicReferenceError("Not Defined", name))
}
return Value{}
}
func (self *_objectStash) deleteBinding(name string) bool {
return self.object.delete(name, false)
}
func (self *_objectStash) outer() _stash {
return self._outer
}
func (self *_objectStash) newReference(name string, strict bool, at _at) _reference {
return newPropertyReference(self._runtime, self.object, name, strict, at)
}
// =========
// _dclStash
// =========
type _dclStash struct {
_runtime *_runtime
_outer _stash
property map[string]_dclProperty
}
type _dclProperty struct {
value Value
mutable bool
deletable bool
readable bool
}
func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash {
return &_dclStash{
_runtime: runtime,
_outer: outer,
property: map[string]_dclProperty{},
}
}
func (in *_dclStash) clone(clone *_clone) _stash {
out, exists := clone.dclStash(in)
if exists {
return out
}
property := make(map[string]_dclProperty, len(in.property))
for index, value := range in.property {
property[index] = clone.dclProperty(value)
}
*out = _dclStash{
clone.runtime,
clone.stash(in._outer),
property,
}
return out
}
func (self *_dclStash) hasBinding(name string) bool {
_, exists := self.property[name]
return exists
}
func (self *_dclStash) runtime() *_runtime {
return self._runtime
}
func (self *_dclStash) createBinding(name string, deletable bool, value Value) {
_, exists := self.property[name]
if exists {
panic(fmt.Errorf("createBinding: %s: already exists", name))
}
self.property[name] = _dclProperty{
value: value,
mutable: true,
deletable: deletable,
readable: false,
}
}
func (self *_dclStash) setBinding(name string, value Value, strict bool) {
property, exists := self.property[name]
if !exists {
panic(fmt.Errorf("setBinding: %s: missing", name))
}
if property.mutable {
property.value = value
self.property[name] = property
} else {
self._runtime.typeErrorResult(strict)
}
}
func (self *_dclStash) setValue(name string, value Value, throw bool) {
if !self.hasBinding(name) {
self.createBinding(name, false, value) // NOT deletable by default
} else {
self.setBinding(name, value, throw)
}
}
// FIXME This is called a __lot__
func (self *_dclStash) getBinding(name string, throw bool) Value {
property, exists := self.property[name]
if !exists {
panic(fmt.Errorf("getBinding: %s: missing", name))
}
if !property.mutable && !property.readable {
if throw { // strict?
panic(self._runtime.panicTypeError())
}
return Value{}
}
return property.value
}
func (self *_dclStash) deleteBinding(name string) bool {
property, exists := self.property[name]
if !exists {
return true
}
if !property.deletable {
return false
}
delete(self.property, name)
return true
}
func (self *_dclStash) outer() _stash {
return self._outer
}
func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference {
return &_stashReference{
name: name,
base: self,
}
}
// ========
// _fnStash
// ========
type _fnStash struct {
_dclStash
arguments *_object
indexOfArgumentName map[string]string
}
func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash {
return &_fnStash{
_dclStash: _dclStash{
_runtime: runtime,
_outer: outer,
property: map[string]_dclProperty{},
},
}
}
func (in *_fnStash) clone(clone *_clone) _stash {
out, exists := clone.fnStash(in)
if exists {
return out
}
dclStash := in._dclStash.clone(clone).(*_dclStash)
index := make(map[string]string, len(in.indexOfArgumentName))
for name, value := range in.indexOfArgumentName {
index[name] = value
}
*out = _fnStash{
_dclStash: *dclStash,
arguments: clone.object(in.arguments),
indexOfArgumentName: index,
}
return out
}
func getStashProperties(stash _stash) (keys []string) {
switch vars := stash.(type) {
case *_dclStash:
for k := range vars.property {
keys = append(keys, k)
}
case *_fnStash:
for k := range vars.property {
keys = append(keys, k)
}
case *_objectStash:
for k := range vars.object.property {
keys = append(keys, k)
}
default:
panic("unknown stash type")
}
return
}

View file

@ -1,106 +0,0 @@
package otto
import (
"strconv"
)
func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash _stash, length int) *_object {
self := runtime.newClassObject("Arguments")
for index, _ := range indexOfParameterName {
name := strconv.FormatInt(int64(index), 10)
objectDefineOwnProperty(self, name, _property{Value{}, 0111}, false)
}
self.objectClass = _classArguments
self.value = _argumentsObject{
indexOfParameterName: indexOfParameterName,
stash: stash,
}
self.prototype = runtime.global.ObjectPrototype
self.defineProperty("length", toValue_int(length), 0101, false)
return self
}
type _argumentsObject struct {
indexOfParameterName []string
// function(abc, def, ghi)
// indexOfParameterName[0] = "abc"
// indexOfParameterName[1] = "def"
// indexOfParameterName[2] = "ghi"
// ...
stash _stash
}
func (in _argumentsObject) clone(clone *_clone) _argumentsObject {
indexOfParameterName := make([]string, len(in.indexOfParameterName))
copy(indexOfParameterName, in.indexOfParameterName)
return _argumentsObject{
indexOfParameterName,
clone.stash(in.stash),
}
}
func (self _argumentsObject) get(name string) (Value, bool) {
index := stringToArrayIndex(name)
if index >= 0 && index < int64(len(self.indexOfParameterName)) {
name := self.indexOfParameterName[index]
if name == "" {
return Value{}, false
}
return self.stash.getBinding(name, false), true
}
return Value{}, false
}
func (self _argumentsObject) put(name string, value Value) {
index := stringToArrayIndex(name)
name = self.indexOfParameterName[index]
self.stash.setBinding(name, value, false)
}
func (self _argumentsObject) delete(name string) {
index := stringToArrayIndex(name)
self.indexOfParameterName[index] = ""
}
func argumentsGet(self *_object, name string) Value {
if value, exists := self.value.(_argumentsObject).get(name); exists {
return value
}
return objectGet(self, name)
}
func argumentsGetOwnProperty(self *_object, name string) *_property {
property := objectGetOwnProperty(self, name)
if value, exists := self.value.(_argumentsObject).get(name); exists {
property.value = value
}
return property
}
func argumentsDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if _, exists := self.value.(_argumentsObject).get(name); exists {
if !objectDefineOwnProperty(self, name, descriptor, false) {
return self.runtime.typeErrorResult(throw)
}
if value, valid := descriptor.value.(Value); valid {
self.value.(_argumentsObject).put(name, value)
}
return true
}
return objectDefineOwnProperty(self, name, descriptor, throw)
}
func argumentsDelete(self *_object, name string, throw bool) bool {
if !objectDelete(self, name, throw) {
return false
}
if _, exists := self.value.(_argumentsObject).get(name); exists {
self.value.(_argumentsObject).delete(name)
}
return true
}

View file

@ -1,109 +0,0 @@
package otto
import (
"strconv"
)
func (runtime *_runtime) newArrayObject(length uint32) *_object {
self := runtime.newObject()
self.class = "Array"
self.defineProperty("length", toValue_uint32(length), 0100, false)
self.objectClass = _classArray
return self
}
func isArray(object *_object) bool {
return object != nil && (object.class == "Array" || object.class == "GoArray")
}
func objectLength(object *_object) uint32 {
if object == nil {
return 0
}
switch object.class {
case "Array":
return object.get("length").value.(uint32)
case "String":
return uint32(object.get("length").value.(int))
case "GoArray":
return uint32(object.get("length").value.(int))
}
return 0
}
func arrayUint32(rt *_runtime, value Value) uint32 {
nm := value.number()
if nm.kind != numberInteger || !isUint32(nm.int64) {
// FIXME
panic(rt.panicRangeError())
}
return uint32(nm.int64)
}
func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
lengthProperty := self.getOwnProperty("length")
lengthValue, valid := lengthProperty.value.(Value)
if !valid {
panic("Array.length != Value{}")
}
length := lengthValue.value.(uint32)
if name == "length" {
if descriptor.value == nil {
return objectDefineOwnProperty(self, name, descriptor, throw)
}
newLengthValue, isValue := descriptor.value.(Value)
if !isValue {
panic(self.runtime.panicTypeError())
}
newLength := arrayUint32(self.runtime, newLengthValue)
descriptor.value = toValue_uint32(newLength)
if newLength > length {
return objectDefineOwnProperty(self, name, descriptor, throw)
}
if !lengthProperty.writable() {
goto Reject
}
newWritable := true
if descriptor.mode&0700 == 0 {
// If writable is off
newWritable = false
descriptor.mode |= 0100
}
if !objectDefineOwnProperty(self, name, descriptor, throw) {
return false
}
for newLength < length {
length--
if !self.delete(strconv.FormatInt(int64(length), 10), false) {
descriptor.value = toValue_uint32(length + 1)
if !newWritable {
descriptor.mode &= 0077
}
objectDefineOwnProperty(self, name, descriptor, false)
goto Reject
}
}
if !newWritable {
descriptor.mode &= 0077
objectDefineOwnProperty(self, name, descriptor, false)
}
} else if index := stringToArrayIndex(name); index >= 0 {
if index >= int64(length) && !lengthProperty.writable() {
goto Reject
}
if !objectDefineOwnProperty(self, strconv.FormatInt(index, 10), descriptor, false) {
goto Reject
}
if index >= int64(length) {
lengthProperty.value = toValue_uint32(uint32(index + 1))
objectDefineOwnProperty(self, "length", *lengthProperty, false)
return true
}
}
return objectDefineOwnProperty(self, name, descriptor, throw)
Reject:
if throw {
panic(self.runtime.panicTypeError())
}
return false
}

View file

@ -1,13 +0,0 @@
package otto
import (
"strconv"
)
func (runtime *_runtime) newBooleanObject(value Value) *_object {
return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool()))
}
func booleanToString(value bool) string {
return strconv.FormatBool(value)
}

View file

@ -1,299 +0,0 @@
package otto
import (
"fmt"
"math"
"regexp"
Time "time"
)
type _dateObject struct {
time Time.Time // Time from the "time" package, a cached version of time
epoch int64
value Value
isNaN bool
}
var (
invalidDateObject = _dateObject{
time: Time.Time{},
epoch: -1,
value: NaNValue(),
isNaN: true,
}
)
type _ecmaTime struct {
year int
month int
day int
hour int
minute int
second int
millisecond int
location *Time.Location // Basically, either local or UTC
}
func ecmaTime(goTime Time.Time) _ecmaTime {
return _ecmaTime{
goTime.Year(),
dateFromGoMonth(goTime.Month()),
goTime.Day(),
goTime.Hour(),
goTime.Minute(),
goTime.Second(),
goTime.Nanosecond() / (100 * 100 * 100),
goTime.Location(),
}
}
func (self *_ecmaTime) goTime() Time.Time {
return Time.Date(
self.year,
dateToGoMonth(self.month),
self.day,
self.hour,
self.minute,
self.second,
self.millisecond*(100*100*100),
self.location,
)
}
func (self *_dateObject) Time() Time.Time {
return self.time
}
func (self *_dateObject) Epoch() int64 {
return self.epoch
}
func (self *_dateObject) Value() Value {
return self.value
}
// FIXME A date should only be in the range of -100,000,000 to +100,000,000 (1970): 15.9.1.1
func (self *_dateObject) SetNaN() {
self.time = Time.Time{}
self.epoch = -1
self.value = NaNValue()
self.isNaN = true
}
func (self *_dateObject) SetTime(time Time.Time) {
self.Set(timeToEpoch(time))
}
func epoch2dateObject(epoch float64) _dateObject {
date := _dateObject{}
date.Set(epoch)
return date
}
func (self *_dateObject) Set(epoch float64) {
// epoch
self.epoch = epochToInteger(epoch)
// time
time, err := epochToTime(epoch)
self.time = time // Is either a valid time, or the zero-value for time.Time
// value & isNaN
if err != nil {
self.isNaN = true
self.epoch = -1
self.value = NaNValue()
} else {
self.value = toValue_int64(self.epoch)
}
}
func epochToInteger(value float64) int64 {
if value > 0 {
return int64(math.Floor(value))
}
return int64(math.Ceil(value))
}
func epochToTime(value float64) (time Time.Time, err error) {
epochWithMilli := value
if math.IsNaN(epochWithMilli) || math.IsInf(epochWithMilli, 0) {
err = fmt.Errorf("Invalid time %v", value)
return
}
epoch := int64(epochWithMilli / 1000)
milli := int64(epochWithMilli) % 1000
time = Time.Unix(int64(epoch), milli*1000000).UTC()
return
}
func timeToEpoch(time Time.Time) float64 {
return float64(time.UnixNano() / (1000 * 1000))
}
func (runtime *_runtime) newDateObject(epoch float64) *_object {
self := runtime.newObject()
self.class = "Date"
// FIXME This is ugly...
date := _dateObject{}
date.Set(epoch)
self.value = date
return self
}
func (self *_object) dateValue() _dateObject {
value, _ := self.value.(_dateObject)
return value
}
func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject {
if _dateObject == nil || _dateObject.class != "Date" {
panic(rt.panicTypeError())
}
return _dateObject.dateValue()
}
// JavaScript is 0-based, Go is 1-based (15.9.1.4)
func dateToGoMonth(month int) Time.Month {
return Time.Month(month + 1)
}
func dateFromGoMonth(month Time.Month) int {
return int(month) - 1
}
// Both JavaScript & Go are 0-based (Sunday == 0)
func dateToGoDay(day int) Time.Weekday {
return Time.Weekday(day)
}
func dateFromGoDay(day Time.Weekday) int {
return int(day)
}
func newDateTime(argumentList []Value, location *Time.Location) (epoch float64) {
pick := func(index int, default_ float64) (float64, bool) {
if index >= len(argumentList) {
return default_, false
}
value := argumentList[index].float64()
if math.IsNaN(value) || math.IsInf(value, 0) {
return 0, true
}
return value, false
}
if len(argumentList) >= 2 { // 2-argument, 3-argument, ...
var year, month, day, hour, minute, second, millisecond float64
var invalid bool
if year, invalid = pick(0, 1900.0); invalid {
goto INVALID
}
if month, invalid = pick(1, 0.0); invalid {
goto INVALID
}
if day, invalid = pick(2, 1.0); invalid {
goto INVALID
}
if hour, invalid = pick(3, 0.0); invalid {
goto INVALID
}
if minute, invalid = pick(4, 0.0); invalid {
goto INVALID
}
if second, invalid = pick(5, 0.0); invalid {
goto INVALID
}
if millisecond, invalid = pick(6, 0.0); invalid {
goto INVALID
}
if year >= 0 && year <= 99 {
year += 1900
}
time := Time.Date(int(year), dateToGoMonth(int(month)), int(day), int(hour), int(minute), int(second), int(millisecond)*1000*1000, location)
return timeToEpoch(time)
} else if len(argumentList) == 0 { // 0-argument
time := Time.Now().UTC()
return timeToEpoch(time)
} else { // 1-argument
value := valueOfArrayIndex(argumentList, 0)
value = toPrimitive(value)
if value.IsString() {
return dateParse(value.string())
}
return value.float64()
}
INVALID:
epoch = math.NaN()
return
}
var (
dateLayoutList = []string{
"2006",
"2006-01",
"2006-01-02",
"2006T15:04",
"2006-01T15:04",
"2006-01-02T15:04",
"2006T15:04:05",
"2006-01T15:04:05",
"2006-01-02T15:04:05",
"2006T15:04:05.000",
"2006-01T15:04:05.000",
"2006-01-02T15:04:05.000",
"2006T15:04-0700",
"2006-01T15:04-0700",
"2006-01-02T15:04-0700",
"2006T15:04:05-0700",
"2006-01T15:04:05-0700",
"2006-01-02T15:04:05-0700",
"2006T15:04:05.000-0700",
"2006-01T15:04:05.000-0700",
"2006-01-02T15:04:05.000-0700",
Time.RFC1123,
}
matchDateTimeZone = regexp.MustCompile(`^(.*)(?:(Z)|([\+\-]\d{2}):(\d{2}))$`)
)
func dateParse(date string) (epoch float64) {
// YYYY-MM-DDTHH:mm:ss.sssZ
var time Time.Time
var err error
{
date := date
if match := matchDateTimeZone.FindStringSubmatch(date); match != nil {
if match[2] == "Z" {
date = match[1] + "+0000"
} else {
date = match[1] + match[3] + match[4]
}
}
for _, layout := range dateLayoutList {
time, err = Time.Parse(layout, date)
if err == nil {
break
}
}
}
if err != nil {
return math.NaN()
}
return float64(time.UnixNano()) / (1000 * 1000) // UnixMilli()
}

View file

@ -1,24 +0,0 @@
package otto
func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object {
self := rt.newClassObject("Error")
if message.IsDefined() {
msg := message.string()
self.defineProperty("message", toValue_string(msg), 0111, false)
self.value = newError(rt, name, stackFramesToPop, msg)
} else {
self.value = newError(rt, name, stackFramesToPop)
}
self.defineOwnProperty("stack", _property{
value: _propertyGetSet{
rt.newNativeFunction("get", "internal", 0, func(FunctionCall) Value {
return toValue_string(self.value.(_error).formatWithStack())
}),
&_nilGetSetObject,
},
mode: modeConfigureMask & modeOnMask,
}, false)
return self
}

View file

@ -1,292 +0,0 @@
package otto
// _constructFunction
type _constructFunction func(*_object, []Value) Value
// 13.2.2 [[Construct]]
func defaultConstruct(fn *_object, argumentList []Value) Value {
object := fn.runtime.newObject()
object.class = "Object"
prototype := fn.get("prototype")
if prototype.kind != valueObject {
prototype = toValue_object(fn.runtime.global.ObjectPrototype)
}
object.prototype = prototype._object()
this := toValue_object(object)
value := fn.call(this, argumentList, false, nativeFrame)
if value.kind == valueObject {
return value
}
return this
}
// _nativeFunction
type _nativeFunction func(FunctionCall) Value
// ===================== //
// _nativeFunctionObject //
// ===================== //
type _nativeFunctionObject struct {
name string
file string
line int
call _nativeFunction // [[Call]]
construct _constructFunction // [[Construct]]
}
func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object {
self := runtime.newClassObject("Function")
self.value = _nativeFunctionObject{
name: name,
file: file,
line: line,
call: native,
construct: defaultConstruct,
}
self.defineProperty("length", toValue_int(length), 0000, false)
return self
}
// =================== //
// _bindFunctionObject //
// =================== //
type _bindFunctionObject struct {
target *_object
this Value
argumentList []Value
}
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
self := runtime.newClassObject("Function")
self.value = _bindFunctionObject{
target: target,
this: this,
argumentList: argumentList,
}
length := int(toInt32(target.get("length")))
length -= len(argumentList)
if length < 0 {
length = 0
}
self.defineProperty("length", toValue_int(length), 0000, false)
self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError
self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError
return self
}
// [[Construct]]
func (fn _bindFunctionObject) construct(argumentList []Value) Value {
object := fn.target
switch value := object.value.(type) {
case _nativeFunctionObject:
return value.construct(object, fn.argumentList)
case _nodeFunctionObject:
argumentList = append(fn.argumentList, argumentList...)
return object.construct(argumentList)
}
panic(fn.target.runtime.panicTypeError())
}
// =================== //
// _nodeFunctionObject //
// =================== //
type _nodeFunctionObject struct {
node *_nodeFunctionLiteral
stash _stash
}
func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object {
self := runtime.newClassObject("Function")
self.value = _nodeFunctionObject{
node: node,
stash: stash,
}
self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false)
return self
}
// ======= //
// _object //
// ======= //
func (self *_object) isCall() bool {
switch fn := self.value.(type) {
case _nativeFunctionObject:
return fn.call != nil
case _bindFunctionObject:
return true
case _nodeFunctionObject:
return true
}
return false
}
func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value {
switch fn := self.value.(type) {
case _nativeFunctionObject:
// Since eval is a native function, we only have to check for it here
if eval {
eval = self == self.runtime.eval // If eval is true, then it IS a direct eval
}
// Enter a scope, name from the native object...
rt := self.runtime
if rt.scope != nil && !eval {
rt.enterFunctionScope(rt.scope.lexical, this)
rt.scope.frame = _frame{
native: true,
nativeFile: fn.file,
nativeLine: fn.line,
callee: fn.name,
file: nil,
}
defer func() {
rt.leaveScope()
}()
}
return fn.call(FunctionCall{
runtime: self.runtime,
eval: eval,
This: this,
ArgumentList: argumentList,
Otto: self.runtime.otto,
})
case _bindFunctionObject:
// TODO Passthrough site, do not enter a scope
argumentList = append(fn.argumentList, argumentList...)
return fn.target.call(fn.this, argumentList, false, frame)
case _nodeFunctionObject:
rt := self.runtime
stash := rt.enterFunctionScope(fn.stash, this)
rt.scope.frame = _frame{
callee: fn.node.name,
file: fn.node.file,
}
defer func() {
rt.leaveScope()
}()
callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList)
if value, valid := callValue.value.(_result); valid {
return value.value
}
return callValue
}
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
}
func (self *_object) construct(argumentList []Value) Value {
switch fn := self.value.(type) {
case _nativeFunctionObject:
if fn.call == nil {
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
}
if fn.construct == nil {
panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self)))
}
return fn.construct(self, argumentList)
case _bindFunctionObject:
return fn.construct(argumentList)
case _nodeFunctionObject:
return defaultConstruct(self, argumentList)
}
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
}
// 15.3.5.3
func (self *_object) hasInstance(of Value) bool {
if !self.isCall() {
// We should not have a hasInstance method
panic(self.runtime.panicTypeError())
}
if !of.IsObject() {
return false
}
prototype := self.get("prototype")
if !prototype.IsObject() {
panic(self.runtime.panicTypeError())
}
prototypeObject := prototype._object()
value := of._object().prototype
for value != nil {
if value == prototypeObject {
return true
}
value = value.prototype
}
return false
}
// ============ //
// FunctionCall //
// ============ //
// FunctionCall is an encapsulation of a JavaScript function call.
type FunctionCall struct {
runtime *_runtime
_thisObject *_object
eval bool // This call is a direct call to eval
This Value
ArgumentList []Value
Otto *Otto
}
// Argument will return the value of the argument at the given index.
//
// If no such argument exists, undefined is returned.
func (self FunctionCall) Argument(index int) Value {
return valueOfArrayIndex(self.ArgumentList, index)
}
func (self FunctionCall) getArgument(index int) (Value, bool) {
return getValueOfArrayIndex(self.ArgumentList, index)
}
func (self FunctionCall) slice(index int) []Value {
if index < len(self.ArgumentList) {
return self.ArgumentList[index:]
}
return []Value{}
}
func (self *FunctionCall) thisObject() *_object {
if self._thisObject == nil {
this := self.This.resolve() // FIXME Is this right?
self._thisObject = self.runtime.toObject(this)
}
return self._thisObject
}
func (self *FunctionCall) thisClassObject(class string) *_object {
thisObject := self.thisObject()
if thisObject.class != class {
panic(self.runtime.panicTypeError())
}
return self._thisObject
}
func (self FunctionCall) toObject(value Value) *_object {
return self.runtime.toObject(value)
}
// CallerLocation will return file location information (file:line:pos) where this function is being called.
func (self FunctionCall) CallerLocation() string {
// see error.go for location()
return self.runtime.scope.outer.frame.location()
}

View file

@ -1,134 +0,0 @@
package otto
import (
"reflect"
"strconv"
)
func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "GoArray"
self.objectClass = _classGoArray
self.value = _newGoArrayObject(value)
return self
}
type _goArrayObject struct {
value reflect.Value
writable bool
propertyMode _propertyMode
}
func _newGoArrayObject(value reflect.Value) *_goArrayObject {
writable := value.Kind() == reflect.Ptr // The Array is addressable (like a Slice)
mode := _propertyMode(0010)
if writable {
mode = 0110
}
self := &_goArrayObject{
value: value,
writable: writable,
propertyMode: mode,
}
return self
}
func (self _goArrayObject) getValue(index int64) (reflect.Value, bool) {
value := reflect.Indirect(self.value)
if index < int64(value.Len()) {
return value.Index(int(index)), true
}
return reflect.Value{}, false
}
func (self _goArrayObject) setValue(index int64, value Value) bool {
indexValue, exists := self.getValue(index)
if !exists {
return false
}
reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind())
if err != nil {
panic(err)
}
indexValue.Set(reflectValue)
return true
}
func goArrayGetOwnProperty(self *_object, name string) *_property {
// length
if name == "length" {
return &_property{
value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()),
mode: 0,
}
}
// .0, .1, .2, ...
index := stringToArrayIndex(name)
if index >= 0 {
object := self.value.(*_goArrayObject)
value := Value{}
reflectValue, exists := object.getValue(index)
if exists {
value = self.runtime.toValue(reflectValue.Interface())
}
return &_property{
value: value,
mode: object.propertyMode,
}
}
return objectGetOwnProperty(self, name)
}
func goArrayEnumerate(self *_object, all bool, each func(string) bool) {
object := self.value.(*_goArrayObject)
// .0, .1, .2, ...
for index, length := 0, object.value.Len(); index < length; index++ {
name := strconv.FormatInt(int64(index), 10)
if !each(name) {
return
}
}
objectEnumerate(self, all, each)
}
func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if name == "length" {
return self.runtime.typeErrorResult(throw)
} else if index := stringToArrayIndex(name); index >= 0 {
object := self.value.(*_goArrayObject)
if object.writable {
if self.value.(*_goArrayObject).setValue(index, descriptor.value.(Value)) {
return true
}
}
return self.runtime.typeErrorResult(throw)
}
return objectDefineOwnProperty(self, name, descriptor, throw)
}
func goArrayDelete(self *_object, name string, throw bool) bool {
// length
if name == "length" {
return self.runtime.typeErrorResult(throw)
}
// .0, .1, .2, ...
index := stringToArrayIndex(name)
if index >= 0 {
object := self.value.(*_goArrayObject)
if object.writable {
indexValue, exists := object.getValue(index)
if exists {
indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem()))
return true
}
}
return self.runtime.typeErrorResult(throw)
}
return self.delete(name, throw)
}

View file

@ -1,87 +0,0 @@
package otto
import (
"reflect"
)
func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "Object" // TODO Should this be something else?
self.objectClass = _classGoMap
self.value = _newGoMapObject(value)
return self
}
type _goMapObject struct {
value reflect.Value
keyKind reflect.Kind
valueKind reflect.Kind
}
func _newGoMapObject(value reflect.Value) *_goMapObject {
if value.Kind() != reflect.Map {
dbgf("%/panic//%@: %v != reflect.Map", value.Kind())
}
self := &_goMapObject{
value: value,
keyKind: value.Type().Key().Kind(),
valueKind: value.Type().Elem().Kind(),
}
return self
}
func (self _goMapObject) toKey(name string) reflect.Value {
reflectValue, err := stringToReflectValue(name, self.keyKind)
if err != nil {
panic(err)
}
return reflectValue
}
func (self _goMapObject) toValue(value Value) reflect.Value {
reflectValue, err := value.toReflectValue(self.valueKind)
if err != nil {
panic(err)
}
return reflectValue
}
func goMapGetOwnProperty(self *_object, name string) *_property {
object := self.value.(*_goMapObject)
value := object.value.MapIndex(object.toKey(name))
if value.IsValid() {
return &_property{self.runtime.toValue(value.Interface()), 0111}
}
return nil
}
func goMapEnumerate(self *_object, all bool, each func(string) bool) {
object := self.value.(*_goMapObject)
keys := object.value.MapKeys()
for _, key := range keys {
if !each(toValue(key).String()) {
return
}
}
}
func goMapDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
object := self.value.(*_goMapObject)
// TODO ...or 0222
if descriptor.mode != 0111 {
return self.runtime.typeErrorResult(throw)
}
if !descriptor.isDataDescriptor() {
return self.runtime.typeErrorResult(throw)
}
object.value.SetMapIndex(object.toKey(name), object.toValue(descriptor.value.(Value)))
return true
}
func goMapDelete(self *_object, name string, throw bool) bool {
object := self.value.(*_goMapObject)
object.value.SetMapIndex(object.toKey(name), reflect.Value{})
// FIXME
return true
}

View file

@ -1,126 +0,0 @@
package otto
import (
"reflect"
"strconv"
)
func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "GoArray" // TODO GoSlice?
self.objectClass = _classGoSlice
self.value = _newGoSliceObject(value)
return self
}
type _goSliceObject struct {
value reflect.Value
}
func _newGoSliceObject(value reflect.Value) *_goSliceObject {
self := &_goSliceObject{
value: value,
}
return self
}
func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) {
if index < int64(self.value.Len()) {
return self.value.Index(int(index)), true
}
return reflect.Value{}, false
}
func (self _goSliceObject) setValue(index int64, value Value) bool {
indexValue, exists := self.getValue(index)
if !exists {
return false
}
reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind())
if err != nil {
panic(err)
}
indexValue.Set(reflectValue)
return true
}
func goSliceGetOwnProperty(self *_object, name string) *_property {
// length
if name == "length" {
return &_property{
value: toValue(self.value.(*_goSliceObject).value.Len()),
mode: 0,
}
}
// .0, .1, .2, ...
index := stringToArrayIndex(name)
if index >= 0 {
value := Value{}
reflectValue, exists := self.value.(*_goSliceObject).getValue(index)
if exists {
value = self.runtime.toValue(reflectValue.Interface())
}
return &_property{
value: value,
mode: 0110,
}
}
// Other methods
if method := self.value.(*_goSliceObject).value.MethodByName(name); (method != reflect.Value{}) {
return &_property{
value: self.runtime.toValue(method.Interface()),
mode: 0110,
}
}
return objectGetOwnProperty(self, name)
}
func goSliceEnumerate(self *_object, all bool, each func(string) bool) {
object := self.value.(*_goSliceObject)
// .0, .1, .2, ...
for index, length := 0, object.value.Len(); index < length; index++ {
name := strconv.FormatInt(int64(index), 10)
if !each(name) {
return
}
}
objectEnumerate(self, all, each)
}
func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if name == "length" {
return self.runtime.typeErrorResult(throw)
} else if index := stringToArrayIndex(name); index >= 0 {
if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) {
return true
}
return self.runtime.typeErrorResult(throw)
}
return objectDefineOwnProperty(self, name, descriptor, throw)
}
func goSliceDelete(self *_object, name string, throw bool) bool {
// length
if name == "length" {
return self.runtime.typeErrorResult(throw)
}
// .0, .1, .2, ...
index := stringToArrayIndex(name)
if index >= 0 {
object := self.value.(*_goSliceObject)
indexValue, exists := object.getValue(index)
if exists {
indexValue.Set(reflect.Zero(object.value.Type().Elem()))
return true
}
return self.runtime.typeErrorResult(throw)
}
return self.delete(name, throw)
}

View file

@ -1,146 +0,0 @@
package otto
import (
"encoding/json"
"reflect"
)
// FIXME Make a note about not being able to modify a struct unless it was
// passed as a pointer-to: &struct{ ... }
// This seems to be a limitation of the reflect package.
// This goes for the other Go constructs too.
// I guess we could get around it by either:
// 1. Creating a new struct every time
// 2. Creating an addressable? struct in the constructor
func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "Object" // TODO Should this be something else?
self.objectClass = _classGoStruct
self.value = _newGoStructObject(value)
return self
}
type _goStructObject struct {
value reflect.Value
}
func _newGoStructObject(value reflect.Value) *_goStructObject {
if reflect.Indirect(value).Kind() != reflect.Struct {
dbgf("%/panic//%@: %v != reflect.Struct", value.Kind())
}
self := &_goStructObject{
value: value,
}
return self
}
func (self _goStructObject) getValue(name string) reflect.Value {
if validGoStructName(name) {
// Do not reveal hidden or unexported fields
if field := reflect.Indirect(self.value).FieldByName(name); (field != reflect.Value{}) {
return field
}
if method := self.value.MethodByName(name); (method != reflect.Value{}) {
return method
}
}
return reflect.Value{}
}
func (self _goStructObject) field(name string) (reflect.StructField, bool) {
return reflect.Indirect(self.value).Type().FieldByName(name)
}
func (self _goStructObject) method(name string) (reflect.Method, bool) {
return reflect.Indirect(self.value).Type().MethodByName(name)
}
func (self _goStructObject) setValue(name string, value Value) bool {
field, exists := self.field(name)
if !exists {
return false
}
fieldValue := self.getValue(name)
reflectValue, err := value.toReflectValue(field.Type.Kind())
if err != nil {
panic(err)
}
fieldValue.Set(reflectValue)
return true
}
func goStructGetOwnProperty(self *_object, name string) *_property {
object := self.value.(*_goStructObject)
value := object.getValue(name)
if value.IsValid() {
return &_property{self.runtime.toValue(value.Interface()), 0110}
}
return objectGetOwnProperty(self, name)
}
func validGoStructName(name string) bool {
if name == "" {
return false
}
return 'A' <= name[0] && name[0] <= 'Z' // TODO What about Unicode?
}
func goStructEnumerate(self *_object, all bool, each func(string) bool) {
object := self.value.(*_goStructObject)
// Enumerate fields
for index := 0; index < reflect.Indirect(object.value).NumField(); index++ {
name := reflect.Indirect(object.value).Type().Field(index).Name
if validGoStructName(name) {
if !each(name) {
return
}
}
}
// Enumerate methods
for index := 0; index < object.value.NumMethod(); index++ {
name := object.value.Type().Method(index).Name
if validGoStructName(name) {
if !each(name) {
return
}
}
}
objectEnumerate(self, all, each)
}
func goStructCanPut(self *_object, name string) bool {
object := self.value.(*_goStructObject)
value := object.getValue(name)
if value.IsValid() {
return true
}
return objectCanPut(self, name)
}
func goStructPut(self *_object, name string, value Value, throw bool) {
object := self.value.(*_goStructObject)
if object.setValue(name, value) {
return
}
objectPut(self, name, value, throw)
}
func goStructMarshalJSON(self *_object) json.Marshaler {
object := self.value.(*_goStructObject)
goValue := reflect.Indirect(object.value).Interface()
switch marshaler := goValue.(type) {
case json.Marshaler:
return marshaler
}
return nil
}

View file

@ -1,5 +0,0 @@
package otto
func (runtime *_runtime) newNumberObject(value Value) *_object {
return runtime.newPrimitiveObject("Number", value.numberValue())
}

View file

@ -1,103 +0,0 @@
package otto
type _reference interface {
invalid() bool // IsUnresolvableReference
getValue() Value // getValue
putValue(Value) string // PutValue
delete() bool
}
// PropertyReference
type _propertyReference struct {
name string
strict bool
base *_object
runtime *_runtime
at _at
}
func newPropertyReference(rt *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference {
return &_propertyReference{
runtime: rt,
name: name,
strict: strict,
base: base,
at: at,
}
}
func (self *_propertyReference) invalid() bool {
return self.base == nil
}
func (self *_propertyReference) getValue() Value {
if self.base == nil {
panic(self.runtime.panicReferenceError("'%s' is not defined", self.name, self.at))
}
return self.base.get(self.name)
}
func (self *_propertyReference) putValue(value Value) string {
if self.base == nil {
return self.name
}
self.base.put(self.name, value, self.strict)
return ""
}
func (self *_propertyReference) delete() bool {
if self.base == nil {
// TODO Throw an error if strict
return true
}
return self.base.delete(self.name, self.strict)
}
// ArgumentReference
func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference {
if base == nil {
panic(hereBeDragons())
}
return newPropertyReference(runtime, base, name, strict, at)
}
type _stashReference struct {
name string
strict bool
base _stash
}
func (self *_stashReference) invalid() bool {
return false // The base (an environment) will never be nil
}
func (self *_stashReference) getValue() Value {
return self.base.getBinding(self.name, self.strict)
}
func (self *_stashReference) putValue(value Value) string {
self.base.setValue(self.name, value, self.strict)
return ""
}
func (self *_stashReference) delete() bool {
if self.base == nil {
// This should never be reached, but just in case
return false
}
return self.base.deleteBinding(self.name)
}
// getIdentifierReference
func getIdentifierReference(runtime *_runtime, stash _stash, name string, strict bool, at _at) _reference {
if stash == nil {
return newPropertyReference(runtime, nil, name, strict, at)
}
if stash.hasBinding(name) {
return stash.newReference(name, strict, at)
}
return getIdentifierReference(runtime, stash.outer(), name, strict, at)
}

View file

@ -1,146 +0,0 @@
package otto
import (
"fmt"
"regexp"
"unicode/utf8"
"github.com/robertkrimen/otto/parser"
)
type _regExpObject struct {
regularExpression *regexp.Regexp
global bool
ignoreCase bool
multiline bool
source string
flags string
}
func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object {
self := runtime.newObject()
self.class = "RegExp"
global := false
ignoreCase := false
multiline := false
re2flags := ""
// TODO Maybe clean up the panicking here... TypeError, SyntaxError, ?
for _, chr := range flags {
switch chr {
case 'g':
if global {
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
}
global = true
case 'm':
if multiline {
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
}
multiline = true
re2flags += "m"
case 'i':
if ignoreCase {
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
}
ignoreCase = true
re2flags += "i"
}
}
re2pattern, err := parser.TransformRegExp(pattern)
if err != nil {
panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error()))
}
if len(re2flags) > 0 {
re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern)
}
regularExpression, err := regexp.Compile(re2pattern)
if err != nil {
panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:]))
}
self.value = _regExpObject{
regularExpression: regularExpression,
global: global,
ignoreCase: ignoreCase,
multiline: multiline,
source: pattern,
flags: flags,
}
self.defineProperty("global", toValue_bool(global), 0, false)
self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false)
self.defineProperty("multiline", toValue_bool(multiline), 0, false)
self.defineProperty("lastIndex", toValue_int(0), 0100, false)
self.defineProperty("source", toValue_string(pattern), 0, false)
return self
}
func (self *_object) regExpValue() _regExpObject {
value, _ := self.value.(_regExpObject)
return value
}
func execRegExp(this *_object, target string) (match bool, result []int) {
if this.class != "RegExp" {
panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object"))
}
lastIndex := this.get("lastIndex").number().int64
index := lastIndex
global := this.get("global").bool()
if !global {
index = 0
}
if 0 > index || index > int64(len(target)) {
} else {
result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:])
}
if result == nil {
//this.defineProperty("lastIndex", toValue_(0), 0111, true)
this.put("lastIndex", toValue_int(0), true)
return // !match
}
match = true
startIndex := index
endIndex := int(lastIndex) + result[1]
// We do this shift here because the .FindStringSubmatchIndex above
// was done on a local subordinate slice of the string, not the whole string
for index, _ := range result {
result[index] += int(startIndex)
}
if global {
//this.defineProperty("lastIndex", toValue_(endIndex), 0111, true)
this.put("lastIndex", toValue_int(endIndex), true)
}
return // match
}
func execResultToArray(runtime *_runtime, target string, result []int) *_object {
captureCount := len(result) / 2
valueArray := make([]Value, captureCount)
for index := 0; index < captureCount; index++ {
offset := 2 * index
if result[offset] != -1 {
valueArray[index] = toValue_string(target[result[offset]:result[offset+1]])
} else {
valueArray[index] = Value{}
}
}
matchIndex := result[0]
if matchIndex != 0 {
matchIndex = 0
// Find the rune index in the string, not the byte index
for index := 0; index < result[0]; {
_, size := utf8.DecodeRuneInString(target[index:])
matchIndex += 1
index += size
}
}
match := runtime.newArrayOf(valueArray)
match.defineProperty("input", toValue_string(target), 0111, false)
match.defineProperty("index", toValue_int(matchIndex), 0111, false)
return match
}

View file

@ -1,112 +0,0 @@
package otto
import (
"strconv"
"unicode/utf8"
)
type _stringObject interface {
Length() int
At(int) rune
String() string
}
type _stringASCII string
func (str _stringASCII) Length() int {
return len(str)
}
func (str _stringASCII) At(at int) rune {
return rune(str[at])
}
func (str _stringASCII) String() string {
return string(str)
}
type _stringWide struct {
string string
length int
runes []rune
}
func (str _stringWide) Length() int {
return str.length
}
func (str _stringWide) At(at int) rune {
if str.runes == nil {
str.runes = []rune(str.string)
}
return str.runes[at]
}
func (str _stringWide) String() string {
return str.string
}
func _newStringObject(str string) _stringObject {
for i := 0; i < len(str); i++ {
if str[i] >= utf8.RuneSelf {
goto wide
}
}
return _stringASCII(str)
wide:
return &_stringWide{
string: str,
length: utf8.RuneCountInString(str),
}
}
func stringAt(str _stringObject, index int) rune {
if 0 <= index && index < str.Length() {
return str.At(index)
}
return utf8.RuneError
}
func (runtime *_runtime) newStringObject(value Value) *_object {
str := _newStringObject(value.string())
self := runtime.newClassObject("String")
self.defineProperty("length", toValue_int(str.Length()), 0, false)
self.objectClass = _classString
self.value = str
return self
}
func (self *_object) stringValue() _stringObject {
if str, ok := self.value.(_stringObject); ok {
return str
}
return nil
}
func stringEnumerate(self *_object, all bool, each func(string) bool) {
if str := self.stringValue(); str != nil {
length := str.Length()
for index := 0; index < length; index++ {
if !each(strconv.FormatInt(int64(index), 10)) {
return
}
}
}
objectEnumerate(self, all, each)
}
func stringGetOwnProperty(self *_object, name string) *_property {
if property := objectGetOwnProperty(self, name); property != nil {
return property
}
// TODO Test a string of length >= +int32 + 1?
if index := stringToArrayIndex(name); index >= 0 {
if chr := stringAt(self.stringValue(), int(index)); chr != utf8.RuneError {
return &_property{toValue_string(string(chr)), 0}
}
}
return nil
}

File diff suppressed because it is too large Load diff

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