mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
console, jsre: replace otto with goja
This commit is contained in:
parent
3271a5afa0
commit
2b360f25c2
172 changed files with 19514 additions and 205492 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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] += "."
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
15
vendor/github.com/dop251/goja/LICENSE
generated
vendored
Normal 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
253
vendor/github.com/dop251/goja/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
goja
|
||||
====
|
||||
|
||||
ECMAScript 5.1(+) implementation in Go.
|
||||
|
||||
[](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
486
vendor/github.com/dop251/goja/array.go
generated
vendored
Normal 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
455
vendor/github.com/dop251/goja/array_sparse.go
generated
vendored
Normal 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
883
vendor/github.com/dop251/goja/builtin_array.go
generated
vendored
Normal 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
50
vendor/github.com/dop251/goja/builtin_boolean.go
generated
vendored
Normal 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
933
vendor/github.com/dop251/goja/builtin_date.go
generated
vendored
Normal 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
62
vendor/github.com/dop251/goja/builtin_error.go
generated
vendored
Normal 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
165
vendor/github.com/dop251/goja/builtin_function.go
generated
vendored
Normal 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
422
vendor/github.com/dop251/goja/builtin_global.go
generated
vendored
Normal 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
520
vendor/github.com/dop251/goja/builtin_json.go
generated
vendored
Normal 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
192
vendor/github.com/dop251/goja/builtin_math.go
generated
vendored
Normal 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
154
vendor/github.com/dop251/goja/builtin_number.go
generated
vendored
Normal 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
412
vendor/github.com/dop251/goja/builtin_object.go
generated
vendored
Normal 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
272
vendor/github.com/dop251/goja/builtin_regexp.go
generated
vendored
Normal 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 := ®expObject{}
|
||||
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
692
vendor/github.com/dop251/goja/builtin_string.go
generated
vendored
Normal 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
105
vendor/github.com/dop251/goja/builtin_typedarrays.go
generated
vendored
Normal 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
461
vendor/github.com/dop251/goja/compiler.go
generated
vendored
Normal 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
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
796
vendor/github.com/dop251/goja/compiler_stmt.go
generated
vendored
Normal 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
104
vendor/github.com/dop251/goja/date.go
generated
vendored
Normal 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
860
vendor/github.com/dop251/goja/date_parser.go
generated
vendored
Normal 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
290
vendor/github.com/dop251/goja/dtoa.go
generated
vendored
Normal 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
240
vendor/github.com/dop251/goja/func.go
generated
vendored
Normal 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
97
vendor/github.com/dop251/goja/ipow.go
generated
vendored
Normal 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
625
vendor/github.com/dop251/goja/object.go
generated
vendored
Normal 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
150
vendor/github.com/dop251/goja/object_args.go
generated
vendored
Normal 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
221
vendor/github.com/dop251/goja/object_gomap.go
generated
vendored
Normal 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
203
vendor/github.com/dop251/goja/object_gomap_reflect.go
generated
vendored
Normal 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
518
vendor/github.com/dop251/goja/object_goreflect.go
generated
vendored
Normal 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
303
vendor/github.com/dop251/goja/object_goslice.go
generated
vendored
Normal 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
250
vendor/github.com/dop251/goja/object_goslice_reflect.go
generated
vendored
Normal 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
200
vendor/github.com/dop251/goja/object_lazy.go
generated
vendored
Normal 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
361
vendor/github.com/dop251/goja/regexp.go
generated
vendored
Normal 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
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
92
vendor/github.com/dop251/goja/srcfile.go
generated
vendored
Normal 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
226
vendor/github.com/dop251/goja/string.go
generated
vendored
Normal 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
313
vendor/github.com/dop251/goja/string_ascii.go
generated
vendored
Normal 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
319
vendor/github.com/dop251/goja/string_unicode.go
generated
vendored
Normal 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
862
vendor/github.com/dop251/goja/value.go
generated
vendored
Normal 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
2521
vendor/github.com/dop251/goja/vm.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
vendor/github.com/robertkrimen/otto/DESIGN.markdown
generated
vendored
1
vendor/github.com/robertkrimen/otto/DESIGN.markdown
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.)
|
||||
7
vendor/github.com/robertkrimen/otto/LICENSE
generated
vendored
7
vendor/github.com/robertkrimen/otto/LICENSE
generated
vendored
|
|
@ -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.
|
||||
63
vendor/github.com/robertkrimen/otto/Makefile
generated
vendored
63
vendor/github.com/robertkrimen/otto/Makefile
generated
vendored
|
|
@ -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 $@
|
||||
|
||||
871
vendor/github.com/robertkrimen/otto/README.markdown
generated
vendored
871
vendor/github.com/robertkrimen/otto/README.markdown
generated
vendored
|
|
@ -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
|
||||
354
vendor/github.com/robertkrimen/otto/builtin.go
generated
vendored
354
vendor/github.com/robertkrimen/otto/builtin.go
generated
vendored
|
|
@ -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()))
|
||||
}
|
||||
681
vendor/github.com/robertkrimen/otto/builtin_array.go
generated
vendored
681
vendor/github.com/robertkrimen/otto/builtin_array.go
generated
vendored
|
|
@ -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())
|
||||
}
|
||||
28
vendor/github.com/robertkrimen/otto/builtin_boolean.go
generated
vendored
28
vendor/github.com/robertkrimen/otto/builtin_boolean.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
615
vendor/github.com/robertkrimen/otto/builtin_date.go
generated
vendored
615
vendor/github.com/robertkrimen/otto/builtin_date.go
generated
vendored
|
|
@ -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
|
||||
126
vendor/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
126
vendor/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
|
|
@ -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)))
|
||||
}
|
||||
129
vendor/github.com/robertkrimen/otto/builtin_function.go
generated
vendored
129
vendor/github.com/robertkrimen/otto/builtin_function.go
generated
vendored
|
|
@ -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))
|
||||
}
|
||||
299
vendor/github.com/robertkrimen/otto/builtin_json.go
generated
vendored
299
vendor/github.com/robertkrimen/otto/builtin_json.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
151
vendor/github.com/robertkrimen/otto/builtin_math.go
generated
vendored
151
vendor/github.com/robertkrimen/otto/builtin_math.go
generated
vendored
|
|
@ -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))
|
||||
}
|
||||
93
vendor/github.com/robertkrimen/otto/builtin_number.go
generated
vendored
93
vendor/github.com/robertkrimen/otto/builtin_number.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
289
vendor/github.com/robertkrimen/otto/builtin_object.go
generated
vendored
289
vendor/github.com/robertkrimen/otto/builtin_object.go
generated
vendored
|
|
@ -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())
|
||||
}
|
||||
65
vendor/github.com/robertkrimen/otto/builtin_regexp.go
generated
vendored
65
vendor/github.com/robertkrimen/otto/builtin_regexp.go
generated
vendored
|
|
@ -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{}
|
||||
}
|
||||
500
vendor/github.com/robertkrimen/otto/builtin_string.go
generated
vendored
500
vendor/github.com/robertkrimen/otto/builtin_string.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
173
vendor/github.com/robertkrimen/otto/clone.go
generated
vendored
173
vendor/github.com/robertkrimen/otto/clone.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
24
vendor/github.com/robertkrimen/otto/cmpl.go
generated
vendored
24
vendor/github.com/robertkrimen/otto/cmpl.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
96
vendor/github.com/robertkrimen/otto/cmpl_evaluate.go
generated
vendored
96
vendor/github.com/robertkrimen/otto/cmpl_evaluate.go
generated
vendored
|
|
@ -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?
|
||||
}
|
||||
}
|
||||
}
|
||||
460
vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go
generated
vendored
460
vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
424
vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go
generated
vendored
424
vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
656
vendor/github.com/robertkrimen/otto/cmpl_parse.go
generated
vendored
656
vendor/github.com/robertkrimen/otto/cmpl_parse.go
generated
vendored
|
|
@ -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() {}
|
||||
51
vendor/github.com/robertkrimen/otto/console.go
generated
vendored
51
vendor/github.com/robertkrimen/otto/console.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
9
vendor/github.com/robertkrimen/otto/dbg.go
generated
vendored
9
vendor/github.com/robertkrimen/otto/dbg.go
generated
vendored
|
|
@ -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()
|
||||
252
vendor/github.com/robertkrimen/otto/error.go
generated
vendored
252
vendor/github.com/robertkrimen/otto/error.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
318
vendor/github.com/robertkrimen/otto/evaluate.go
generated
vendored
318
vendor/github.com/robertkrimen/otto/evaluate.go
generated
vendored
|
|
@ -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))
|
||||
}
|
||||
221
vendor/github.com/robertkrimen/otto/global.go
generated
vendored
221
vendor/github.com/robertkrimen/otto/global.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
6649
vendor/github.com/robertkrimen/otto/inline.go
generated
vendored
6649
vendor/github.com/robertkrimen/otto/inline.go
generated
vendored
File diff suppressed because it is too large
Load diff
1086
vendor/github.com/robertkrimen/otto/inline.pl
generated
vendored
1086
vendor/github.com/robertkrimen/otto/inline.pl
generated
vendored
File diff suppressed because it is too large
Load diff
156
vendor/github.com/robertkrimen/otto/object.go
generated
vendored
156
vendor/github.com/robertkrimen/otto/object.go
generated
vendored
|
|
@ -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:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
493
vendor/github.com/robertkrimen/otto/object_class.go
generated
vendored
493
vendor/github.com/robertkrimen/otto/object_class.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
770
vendor/github.com/robertkrimen/otto/otto.go
generated
vendored
770
vendor/github.com/robertkrimen/otto/otto.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
178
vendor/github.com/robertkrimen/otto/otto_.go
generated
vendored
178
vendor/github.com/robertkrimen/otto/otto_.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
220
vendor/github.com/robertkrimen/otto/property.go
generated
vendored
220
vendor/github.com/robertkrimen/otto/property.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
30
vendor/github.com/robertkrimen/otto/result.go
generated
vendored
30
vendor/github.com/robertkrimen/otto/result.go
generated
vendored
|
|
@ -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}
|
||||
}
|
||||
711
vendor/github.com/robertkrimen/otto/runtime.go
generated
vendored
711
vendor/github.com/robertkrimen/otto/runtime.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
35
vendor/github.com/robertkrimen/otto/scope.go
generated
vendored
35
vendor/github.com/robertkrimen/otto/scope.go
generated
vendored
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
119
vendor/github.com/robertkrimen/otto/script.go
generated
vendored
119
vendor/github.com/robertkrimen/otto/script.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
296
vendor/github.com/robertkrimen/otto/stash.go
generated
vendored
296
vendor/github.com/robertkrimen/otto/stash.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
106
vendor/github.com/robertkrimen/otto/type_arguments.go
generated
vendored
106
vendor/github.com/robertkrimen/otto/type_arguments.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
109
vendor/github.com/robertkrimen/otto/type_array.go
generated
vendored
109
vendor/github.com/robertkrimen/otto/type_array.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
13
vendor/github.com/robertkrimen/otto/type_boolean.go
generated
vendored
13
vendor/github.com/robertkrimen/otto/type_boolean.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
299
vendor/github.com/robertkrimen/otto/type_date.go
generated
vendored
299
vendor/github.com/robertkrimen/otto/type_date.go
generated
vendored
|
|
@ -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()
|
||||
}
|
||||
24
vendor/github.com/robertkrimen/otto/type_error.go
generated
vendored
24
vendor/github.com/robertkrimen/otto/type_error.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
292
vendor/github.com/robertkrimen/otto/type_function.go
generated
vendored
292
vendor/github.com/robertkrimen/otto/type_function.go
generated
vendored
|
|
@ -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()
|
||||
}
|
||||
134
vendor/github.com/robertkrimen/otto/type_go_array.go
generated
vendored
134
vendor/github.com/robertkrimen/otto/type_go_array.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
87
vendor/github.com/robertkrimen/otto/type_go_map.go
generated
vendored
87
vendor/github.com/robertkrimen/otto/type_go_map.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
126
vendor/github.com/robertkrimen/otto/type_go_slice.go
generated
vendored
126
vendor/github.com/robertkrimen/otto/type_go_slice.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
146
vendor/github.com/robertkrimen/otto/type_go_struct.go
generated
vendored
146
vendor/github.com/robertkrimen/otto/type_go_struct.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
5
vendor/github.com/robertkrimen/otto/type_number.go
generated
vendored
5
vendor/github.com/robertkrimen/otto/type_number.go
generated
vendored
|
|
@ -1,5 +0,0 @@
|
|||
package otto
|
||||
|
||||
func (runtime *_runtime) newNumberObject(value Value) *_object {
|
||||
return runtime.newPrimitiveObject("Number", value.numberValue())
|
||||
}
|
||||
103
vendor/github.com/robertkrimen/otto/type_reference.go
generated
vendored
103
vendor/github.com/robertkrimen/otto/type_reference.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
146
vendor/github.com/robertkrimen/otto/type_regexp.go
generated
vendored
146
vendor/github.com/robertkrimen/otto/type_regexp.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
112
vendor/github.com/robertkrimen/otto/type_string.go
generated
vendored
112
vendor/github.com/robertkrimen/otto/type_string.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
1033
vendor/github.com/robertkrimen/otto/value.go
generated
vendored
1033
vendor/github.com/robertkrimen/otto/value.go
generated
vendored
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
Loading…
Reference in a new issue