mirror of
https://github.com/robertkrimen/otto
synced 2025-10-19 19:55:30 +08:00
Add Go <=> JavaScript type interaction
Via reflection for struct, map, and slice/array Fix #10
This commit is contained in:
parent
96601bf274
commit
a879744c20
3
Makefile
3
Makefile
|
|
@ -32,6 +32,9 @@ TEST := -v --run RegExp_exec
|
|||
TEST := -v --run _panic
|
||||
TEST := -v --run TransformRegExp
|
||||
TEST := -v --run Lexer
|
||||
TEST := -v --run Reflect
|
||||
TEST := -v --run _reflectSlice
|
||||
TEST := -v --run _reflect
|
||||
TEST := .
|
||||
|
||||
test: test-i
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ Embedding a Go function in JavaScript:
|
|||
|
||||
Otto.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
|
||||
right, _ := call.Argument(0).ToInteger()
|
||||
result, _ := otto.ToValue(2 + right)
|
||||
result, _ := Otto.ToValue(2 + right)
|
||||
return result
|
||||
})
|
||||
|
||||
|
|
@ -280,6 +280,12 @@ conversion failing), 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)
|
||||
```
|
||||
|
||||
#### type Value
|
||||
|
||||
```go
|
||||
|
|
|
|||
7
dbg.go
Normal file
7
dbg.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package otto
|
||||
|
||||
import (
|
||||
Dbg "github.com/robertkrimen/otto/dbg"
|
||||
)
|
||||
|
||||
var dbg, dbgf = Dbg.New()
|
||||
361
dbg/dbg.go
Normal file
361
dbg/dbg.go
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
Package dbg is a println/printf/log-debugging utility library.
|
||||
|
||||
import (
|
||||
Dbg "github.com/robertkrimen/dbg"
|
||||
)
|
||||
|
||||
dbg, dbgf := Dbg.New()
|
||||
|
||||
dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi)
|
||||
# "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793"
|
||||
|
||||
dbgf("With a %s formatting %.2f", "little", math.Pi)
|
||||
# "2013/01/28 16:51:55 With a little formatting (3.14)"
|
||||
|
||||
dbgf("%/fatal//A fatal debug statement: should not be here")
|
||||
# "A fatal debug statement: should not be here"
|
||||
# ...and then, os.Exit(1)
|
||||
|
||||
dbgf("%/panic//Can also panic %s", "this")
|
||||
# "Can also panic this"
|
||||
# ...as a panic, equivalent to: panic("Can also panic this")
|
||||
|
||||
dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()")
|
||||
# "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()"
|
||||
|
||||
dbgf("%d %d", 1, 2, 3, 4, 5)
|
||||
# "2013/01/28 17:16:32 Another example: 1 2 3 4 5"
|
||||
|
||||
dbgf("%@: Include the function name for a little context (via %s)", "%@")
|
||||
# "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)"
|
||||
|
||||
By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output.
|
||||
However, you can also provide your own output destination by invoking dbg.New with
|
||||
a customization function:
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
Dbg "github.com/robertkrimen/dbg"
|
||||
"os"
|
||||
)
|
||||
|
||||
# dbg to os.Stderr
|
||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
||||
dbgr.SetOutput(os.Stderr)
|
||||
})
|
||||
|
||||
# A slightly contrived example:
|
||||
var buffer bytes.Buffer
|
||||
dbg, dbgf := New(func(dbgr *Dbgr) {
|
||||
dbgr.SetOutput(&buffer)
|
||||
})
|
||||
|
||||
*/
|
||||
package dbg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type _frmt struct {
|
||||
ctl string
|
||||
format string
|
||||
operandCount int
|
||||
panic bool
|
||||
fatal bool
|
||||
}
|
||||
|
||||
var (
|
||||
ctlTest = regexp.MustCompile(`^\s*%/`)
|
||||
ctlScan = regexp.MustCompile(`%?/(panic|fatal)(?:\s|$)`)
|
||||
)
|
||||
|
||||
func operandCount(format string) int {
|
||||
count := 0
|
||||
end := len(format)
|
||||
for at := 0; at < end; {
|
||||
for at < end && format[at] != '%' {
|
||||
at++
|
||||
}
|
||||
at++
|
||||
if at < end {
|
||||
if format[at] != '%' && format[at] != '@' {
|
||||
count++
|
||||
}
|
||||
at++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func parseFormat(format string) (frmt _frmt) {
|
||||
if ctlTest.MatchString(format) {
|
||||
format = strings.TrimLeftFunc(format, unicode.IsSpace)
|
||||
index := strings.Index(format, "//")
|
||||
if index != -1 {
|
||||
frmt.ctl = format[0:index]
|
||||
format = format[index+2:] // Skip the second slash via +2 (instead of +1)
|
||||
} else {
|
||||
frmt.ctl = format
|
||||
format = ""
|
||||
}
|
||||
for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) {
|
||||
for _, value := range tmp[1:] {
|
||||
switch value {
|
||||
case "panic":
|
||||
frmt.panic = true
|
||||
case "fatal":
|
||||
frmt.fatal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
frmt.format = format
|
||||
frmt.operandCount = operandCount(format)
|
||||
return
|
||||
}
|
||||
|
||||
type Dbgr struct {
|
||||
emit _emit
|
||||
}
|
||||
|
||||
type DbgFunction func(values ...interface{})
|
||||
|
||||
func NewDbgr() *Dbgr {
|
||||
self := &Dbgr{}
|
||||
return self
|
||||
}
|
||||
|
||||
/*
|
||||
New will create and return a pair of debugging functions. You can customize where
|
||||
they output to by passing in an (optional) customization function:
|
||||
|
||||
import (
|
||||
Dbg "github.com/robertkrimen/dbg"
|
||||
"os"
|
||||
)
|
||||
|
||||
# dbg to os.Stderr
|
||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
||||
dbgr.SetOutput(os.Stderr)
|
||||
})
|
||||
|
||||
*/
|
||||
func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) {
|
||||
dbgr := NewDbgr()
|
||||
if len(options) > 0 {
|
||||
if fn, ok := options[0].(func(*Dbgr)); ok {
|
||||
fn(dbgr)
|
||||
}
|
||||
}
|
||||
return dbgr.DbgDbgf()
|
||||
}
|
||||
|
||||
func (self Dbgr) Dbg(values ...interface{}) {
|
||||
self.getEmit().emit(_frmt{}, "", values...)
|
||||
}
|
||||
|
||||
func (self Dbgr) Dbgf(values ...interface{}) {
|
||||
self.dbgf(values...)
|
||||
}
|
||||
|
||||
func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) {
|
||||
dbg = func(vl ...interface{}) {
|
||||
self.Dbg(vl...)
|
||||
}
|
||||
dbgf = func(vl ...interface{}) {
|
||||
self.dbgf(vl...)
|
||||
}
|
||||
return dbg, dbgf // Redundant, but...
|
||||
}
|
||||
|
||||
func (self Dbgr) dbgf(values ...interface{}) {
|
||||
var frmt _frmt
|
||||
if len(values) > 0 {
|
||||
tmp := fmt.Sprint(values[0])
|
||||
frmt = parseFormat(tmp)
|
||||
values = values[1:]
|
||||
}
|
||||
|
||||
buffer_f := bytes.Buffer{}
|
||||
format := frmt.format
|
||||
end := len(format)
|
||||
for at := 0; at < end; {
|
||||
last := at
|
||||
for at < end && format[at] != '%' {
|
||||
at++
|
||||
}
|
||||
if at > last {
|
||||
buffer_f.WriteString(format[last:at])
|
||||
}
|
||||
if at >= end {
|
||||
break
|
||||
}
|
||||
// format[at] == '%'
|
||||
at++
|
||||
// format[at] == ?
|
||||
if format[at] == '@' {
|
||||
depth := 2
|
||||
pc, _, _, _ := runtime.Caller(depth)
|
||||
name := runtime.FuncForPC(pc).Name()
|
||||
buffer_f.WriteString(name)
|
||||
} else {
|
||||
buffer_f.WriteString(format[at-1 : at+1])
|
||||
}
|
||||
at++
|
||||
}
|
||||
|
||||
values_f := values[0:frmt.operandCount]
|
||||
values_dbg := values[frmt.operandCount:]
|
||||
if len(values_dbg) > 0 {
|
||||
// Adjust frmt.format:
|
||||
{
|
||||
tmp := format
|
||||
if len(tmp) > 0 {
|
||||
if unicode.IsSpace(rune(tmp[len(tmp)-1])) {
|
||||
buffer_f.WriteString("%s")
|
||||
} else {
|
||||
buffer_f.WriteString(" %s")
|
||||
}
|
||||
} else {
|
||||
buffer_f.WriteString("%s")
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust vl_f:
|
||||
{
|
||||
tmp := []string{}
|
||||
for _, value := range values_dbg {
|
||||
tmp = append(tmp, fmt.Sprintf("%v", value))
|
||||
}
|
||||
values_f = append(values_f, strings.Join(tmp, " "))
|
||||
}
|
||||
}
|
||||
|
||||
format = buffer_f.String()
|
||||
self.getEmit().emit(frmt, format, values_f...)
|
||||
}
|
||||
|
||||
// Idiot-proof &Dbgr{}, etc.
|
||||
func (self *Dbgr) getEmit() _emit {
|
||||
if self.emit == nil {
|
||||
self.emit = standardEmit()
|
||||
}
|
||||
return self.emit
|
||||
}
|
||||
|
||||
// SetOutput will accept the following as a destination for output:
|
||||
//
|
||||
// *log.Logger Print*/Panic*/Fatal* of the logger
|
||||
// io.Writer -
|
||||
// nil Reset to the default output (os.Stderr)
|
||||
// "log" Print*/Panic*/Fatal* via the "log" package
|
||||
//
|
||||
func (self *Dbgr) SetOutput(output interface{}) {
|
||||
if output == nil {
|
||||
self.emit = standardEmit()
|
||||
return
|
||||
}
|
||||
switch output := output.(type) {
|
||||
case *log.Logger:
|
||||
self.emit = _emitLogger{
|
||||
logger: output,
|
||||
}
|
||||
return
|
||||
case io.Writer:
|
||||
self.emit = _emitWriter{
|
||||
writer: output,
|
||||
}
|
||||
return
|
||||
case string:
|
||||
if output == "log" {
|
||||
self.emit = _emitLog{}
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(output)
|
||||
}
|
||||
|
||||
// ======== //
|
||||
// = emit = //
|
||||
// ======== //
|
||||
|
||||
func standardEmit() _emit {
|
||||
return _emitWriter{
|
||||
writer: os.Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
func ln(tmp string) string {
|
||||
length := len(tmp)
|
||||
if length > 0 && tmp[length-1] != '\n' {
|
||||
return tmp + "\n"
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
type _emit interface {
|
||||
emit(_frmt, string, ...interface{})
|
||||
}
|
||||
|
||||
type _emitWriter struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) {
|
||||
if format == "" {
|
||||
fmt.Fprintln(self.writer, values...)
|
||||
} else {
|
||||
if frmt.panic {
|
||||
panic(fmt.Sprintf(format, values...))
|
||||
}
|
||||
fmt.Fprintf(self.writer, ln(format), values...)
|
||||
if frmt.fatal {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type _emitLogger struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) {
|
||||
if format == "" {
|
||||
self.logger.Println(values...)
|
||||
} else {
|
||||
if frmt.panic {
|
||||
self.logger.Panicf(format, values...)
|
||||
} else if frmt.fatal {
|
||||
self.logger.Fatalf(format, values...)
|
||||
} else {
|
||||
self.logger.Printf(format, values...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type _emitLog struct {
|
||||
}
|
||||
|
||||
func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) {
|
||||
if format == "" {
|
||||
log.Println(values...)
|
||||
} else {
|
||||
if frmt.panic {
|
||||
log.Panicf(format, values...)
|
||||
} else if frmt.fatal {
|
||||
log.Fatalf(format, values...)
|
||||
} else {
|
||||
log.Printf(format, values...)
|
||||
}
|
||||
}
|
||||
}
|
||||
34
otto.go
34
otto.go
|
|
@ -49,7 +49,7 @@ Embedding a Go function in JavaScript:
|
|||
|
||||
Otto.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
|
||||
right, _ := call.Argument(0).ToInteger()
|
||||
result, _ := otto.ToValue(2 + right)
|
||||
result, _ := Otto.ToValue(2 + right)
|
||||
return result
|
||||
})
|
||||
|
||||
|
|
@ -201,10 +201,16 @@ func (self Otto) getValue(name string) Value {
|
|||
//
|
||||
// If the top-level binding does not exist, it will be created.
|
||||
func (self Otto) Set(name string, value interface{}) error {
|
||||
err := catchPanic(func() {
|
||||
self.setValue(name, self.runtime.toValue(value))
|
||||
})
|
||||
return err
|
||||
{
|
||||
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) {
|
||||
|
|
@ -242,6 +248,10 @@ func (self Otto) Object(source string) (*Object, error) {
|
|||
return nil, fmt.Errorf("Result was not an object")
|
||||
}
|
||||
|
||||
func (self Otto) ToValue(value interface{}) (Value, error) {
|
||||
return self.runtime.ToValue(value)
|
||||
}
|
||||
|
||||
// Object{}
|
||||
|
||||
// Object is the representation of a JavaScript object.
|
||||
|
|
@ -296,10 +306,16 @@ func (self Object) Get(name string) (Value, error) {
|
|||
// 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 {
|
||||
err := catchPanic(func() {
|
||||
self.object.put(name, self.object.runtime.toValue(value), true)
|
||||
})
|
||||
return err
|
||||
{
|
||||
value, err := self.object.runtime.ToValue(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = catchPanic(func() {
|
||||
self.object.put(name, value, true)
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Class will return the class string of the object.
|
||||
|
|
|
|||
10
otto_.go
10
otto_.go
|
|
@ -15,7 +15,7 @@ func isIdentifier(string_ string) bool {
|
|||
return isIdentifier_Regexp.MatchString(string_)
|
||||
}
|
||||
|
||||
func toValueArray(arguments ...interface{}) []Value {
|
||||
func (self *_runtime) toValueArray(arguments ...interface{}) []Value {
|
||||
length := len(arguments)
|
||||
if length == 1 {
|
||||
if valueArray, ok := arguments[0].([]Value); ok {
|
||||
|
|
@ -86,14 +86,6 @@ func valueToArrayIndex(indexValue Value, length uint, negativeIsZero bool) uint
|
|||
}
|
||||
}
|
||||
|
||||
func dbg(arguments ...interface{}) {
|
||||
output := []string{}
|
||||
for _, argument := range arguments {
|
||||
output = append(output, fmt.Sprintf("%v", argument))
|
||||
}
|
||||
fmt.Println(strings.Join(output, " "))
|
||||
}
|
||||
|
||||
func boolFields(input string) (result map[string]bool) {
|
||||
result = map[string]bool{}
|
||||
for _, word := range strings.Fields(input) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestOttoError(t *testing.T) {
|
|||
Is(err, "TypeError: Nothing happens.")
|
||||
|
||||
_, err = ToValue([]byte{})
|
||||
Is(err, "TypeError: Unable to convert value: [] ([]uint8)")
|
||||
Is(err, "TypeError: Invalid value (slice): Missing runtime: [] ([]uint8)")
|
||||
|
||||
_, err = Otto.Run(`
|
||||
(function(){
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ import (
|
|||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var testOtto *Otto
|
||||
|
||||
func runTestWithOtto() (*Otto, func(string, ...interface{}) Value) {
|
||||
Otto := New()
|
||||
testOtto = Otto
|
||||
test := func(name string, expect ...interface{}) Value {
|
||||
raise := false
|
||||
defer func() {
|
||||
|
|
|
|||
290
reflect_test.go
Normal file
290
reflect_test.go
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
package otto
|
||||
|
||||
import (
|
||||
. "github.com/robertkrimen/terst"
|
||||
"math"
|
||||
//"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func failSet(name string, value interface{}) {
|
||||
err := testOtto.Set(name, value)
|
||||
Is(err, nil)
|
||||
if err != nil {
|
||||
Terst().TestingT.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
type testStruct struct {
|
||||
Abc bool
|
||||
Def int
|
||||
Ghi string
|
||||
}
|
||||
|
||||
func TestReflect(t *testing.T) {
|
||||
Terst(t)
|
||||
|
||||
if false {
|
||||
// Testing dbgf
|
||||
// These should panic
|
||||
toValue("Xyzzy").toReflectValue(reflect.Ptr)
|
||||
stringToReflectValue("Xyzzy", reflect.Ptr)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflectStruct(t *testing.T) {
|
||||
Terst(t)
|
||||
|
||||
_, test := runTestWithOtto()
|
||||
|
||||
// testStruct
|
||||
{
|
||||
abc := &testStruct{}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
[ abc.Abc, abc.Ghi ]
|
||||
`, "false,")
|
||||
|
||||
abc.Abc = true
|
||||
abc.Ghi = "Nothing happens."
|
||||
|
||||
test(`
|
||||
[ abc.Abc, abc.Ghi ]
|
||||
`, "true,Nothing happens.")
|
||||
|
||||
*abc = testStruct{}
|
||||
|
||||
test(`
|
||||
[ abc.Abc, abc.Ghi ]
|
||||
`, "false,")
|
||||
|
||||
abc.Abc = true
|
||||
abc.Ghi = "Xyzzy"
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
[ abc.Abc, abc.Ghi ]
|
||||
`, "true,Xyzzy")
|
||||
|
||||
Is(abc.Abc, true)
|
||||
test(`
|
||||
abc.Abc = false;
|
||||
abc.Def = 451;
|
||||
abc.Ghi = "Nothing happens.";
|
||||
abc.abc = "Something happens.";
|
||||
[ abc.Def, abc.abc ];
|
||||
`, "451,Something happens.")
|
||||
Is(abc.Abc, false)
|
||||
Is(abc.Def, 451)
|
||||
Is(abc.Ghi, "Nothing happens.")
|
||||
|
||||
test(`
|
||||
delete abc.Def;
|
||||
delete abc.abc;
|
||||
[ abc.Def, abc.abc ];
|
||||
`, "451,")
|
||||
Is(abc.Def, 451)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflectMap(t *testing.T) {
|
||||
Terst(t)
|
||||
|
||||
_, test := runTestWithOtto()
|
||||
|
||||
// map[string]string
|
||||
{
|
||||
abc := map[string]string{
|
||||
"Xyzzy": "Nothing happens.",
|
||||
"def": "1",
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc.xyz = "pqr";
|
||||
[ abc.Xyzzy, abc.def, abc.ghi ];
|
||||
`, "Nothing happens.,1,")
|
||||
|
||||
Is(abc["xyz"], "pqr")
|
||||
}
|
||||
|
||||
// map[string]float64
|
||||
{
|
||||
abc := map[string]float64{
|
||||
"Xyzzy": math.Pi,
|
||||
"def": 1,
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc.xyz = "pqr";
|
||||
abc.jkl = 10;
|
||||
[ abc.Xyzzy, abc.def, abc.ghi ];
|
||||
`, "3.141592653589793,1,")
|
||||
|
||||
Is(abc["xyz"], "NaN")
|
||||
Is(abc["jkl"], float64(10))
|
||||
}
|
||||
|
||||
// map[string]int32
|
||||
{
|
||||
abc := map[string]int32{
|
||||
"Xyzzy": 3,
|
||||
"def": 1,
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc.xyz = "pqr";
|
||||
abc.jkl = 10;
|
||||
[ abc.Xyzzy, abc.def, abc.ghi ];
|
||||
`, "3,1,")
|
||||
|
||||
Is(abc["xyz"], 0)
|
||||
Is(abc["jkl"], int32(10))
|
||||
|
||||
test(`
|
||||
delete abc["Xyzzy"];
|
||||
`)
|
||||
|
||||
_, exists := abc["Xyzzy"]
|
||||
IsFalse(exists)
|
||||
Is(abc["Xyzzy"], 0)
|
||||
}
|
||||
|
||||
// map[int32]string
|
||||
{
|
||||
abc := map[int32]string{
|
||||
0: "abc",
|
||||
1: "def",
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc[2] = "pqr";
|
||||
//abc.jkl = 10;
|
||||
abc[3] = 10;
|
||||
[ abc[0], abc[1], abc[2], abc[3] ]
|
||||
`, "abc,def,pqr,10")
|
||||
|
||||
Is(abc[2], "pqr")
|
||||
Is(abc[3], "10")
|
||||
|
||||
test(`
|
||||
delete abc[2];
|
||||
`)
|
||||
|
||||
_, exists := abc[2]
|
||||
IsFalse(exists)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflectSlice(t *testing.T) {
|
||||
Terst(t)
|
||||
|
||||
_, test := runTestWithOtto()
|
||||
|
||||
// []bool
|
||||
{
|
||||
abc := []bool{
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc
|
||||
`, "false,true,true,false")
|
||||
|
||||
test(`
|
||||
abc[0] = true
|
||||
abc[abc.length-1] = true
|
||||
abc
|
||||
`, "true,true,true,true")
|
||||
|
||||
Is(abc, []bool{true, true, true, true})
|
||||
Is(abc[len(abc)-1], true)
|
||||
}
|
||||
|
||||
// []int32
|
||||
{
|
||||
abc := make([]int32, 4)
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc
|
||||
`, "0,0,0,0")
|
||||
|
||||
test(`
|
||||
abc[0] = 4.2
|
||||
abc[1] = "42"
|
||||
abc[2] = 3.14
|
||||
abc
|
||||
`, "4,42,3,0")
|
||||
|
||||
Is(abc, []int32{4, 42, 3, 0})
|
||||
|
||||
test(`
|
||||
delete abc[1]
|
||||
delete abc[2]
|
||||
`)
|
||||
Is(abc[1], 0)
|
||||
Is(abc[2], 0)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflectArray(t *testing.T) {
|
||||
Terst(t)
|
||||
|
||||
_, test := runTestWithOtto()
|
||||
|
||||
// []bool
|
||||
{
|
||||
abc := [4]bool{
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
}
|
||||
failSet("abc", abc)
|
||||
|
||||
test(`
|
||||
abc
|
||||
`, "false,true,true,false")
|
||||
// Unaddressable array
|
||||
|
||||
test(`
|
||||
abc[0] = true
|
||||
abc[abc.length-1] = true
|
||||
abc
|
||||
`, "false,true,true,false")
|
||||
// Again, unaddressable array
|
||||
|
||||
Is(abc, [4]bool{false, true, true, false})
|
||||
Is(abc[len(abc)-1], false)
|
||||
// ...
|
||||
}
|
||||
|
||||
// []int32
|
||||
{
|
||||
abc := make([]int32, 4)
|
||||
failSet("abc", &abc) // Addressable
|
||||
|
||||
test(`
|
||||
abc
|
||||
`, "0,0,0,0")
|
||||
|
||||
test(`
|
||||
abc[0] = 4.2
|
||||
abc[1] = "42"
|
||||
abc[2] = 3.14
|
||||
abc
|
||||
`, "4,42,3,0")
|
||||
|
||||
Is(abc, []int32{4, 42, 3, 0})
|
||||
}
|
||||
}
|
||||
26
runtime.go
26
runtime.go
|
|
@ -1,8 +1,8 @@
|
|||
package otto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type _runtime struct {
|
||||
|
|
@ -320,12 +320,36 @@ func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func (self *_runtime) ToValue(value interface{}) (Value, error) {
|
||||
result := UndefinedValue()
|
||||
err := catchPanic(func() {
|
||||
result = self.toValue(value)
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (self *_runtime) toValue(value interface{}) Value {
|
||||
switch value := value.(type) {
|
||||
case func(FunctionCall) Value:
|
||||
return toValue(self.newNativeFunction(value, 0, "nativeFunction"))
|
||||
case _nativeFunction:
|
||||
return toValue(self.newNativeFunction(value, 0, "nativeFunction"))
|
||||
case Object, *Object, _object, *_object:
|
||||
// Nothing happens.
|
||||
default:
|
||||
{
|
||||
value := reflect.Indirect(reflect.ValueOf(value))
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
return toValue(self.newGoStructObject(value))
|
||||
case reflect.Map:
|
||||
return toValue(self.newGoMapObject(value))
|
||||
case reflect.Slice, reflect.Array:
|
||||
object := self.newGoArrayObject(value)
|
||||
object.prototype = self.Global.ArrayPrototype
|
||||
return toValue(object)
|
||||
}
|
||||
}
|
||||
}
|
||||
return toValue(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,18 +64,6 @@ func (self *_arrayStash) get(name string) Value {
|
|||
return self._stash.get(name)
|
||||
}
|
||||
|
||||
func (self *_arrayStash) enumerate(each func(string)) {
|
||||
// .0, .1, .2, ...
|
||||
for index, _ := range self.valueArray {
|
||||
if self.valueArray[index]._valueType == valueEmpty {
|
||||
continue // A sparse array
|
||||
}
|
||||
name := strconv.FormatInt(int64(index), 10)
|
||||
each(name)
|
||||
}
|
||||
self._stash.enumerate(each)
|
||||
}
|
||||
|
||||
func (self *_arrayStash) property(name string) *_property {
|
||||
// length
|
||||
if name == "length" {
|
||||
|
|
@ -101,6 +89,18 @@ func (self *_arrayStash) property(name string) *_property {
|
|||
return self._stash.property(name)
|
||||
}
|
||||
|
||||
func (self *_arrayStash) enumerate(each func(string)) {
|
||||
// .0, .1, .2, ...
|
||||
for index, _ := range self.valueArray {
|
||||
if self.valueArray[index]._valueType == valueEmpty {
|
||||
continue // A sparse array
|
||||
}
|
||||
name := strconv.FormatInt(int64(index), 10)
|
||||
each(name)
|
||||
}
|
||||
self._stash.enumerate(each)
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
func (self *_arrayStash) canPut(name string) bool {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func (self *_object) Call(this Value, argumentList ...interface{}) Value {
|
|||
if self._Function == nil {
|
||||
panic(newTypeError("%v is not a function", toValue(self)))
|
||||
}
|
||||
return self.runtime.Call(self, this, toValueArray(argumentList...))
|
||||
return self.runtime.Call(self, this, self.runtime.toValueArray(argumentList...))
|
||||
// ... -> runtime -> self.Function.Call.Dispatch -> ...
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ func (self *_object) Construct(this Value, argumentList ...interface{}) Value {
|
|||
if self._Function == nil {
|
||||
panic(newTypeError("%v is not a function", toValue(self)))
|
||||
}
|
||||
return self._Function.Construct(self, this, toValueArray(argumentList...))
|
||||
return self._Function.Construct(self, this, self.runtime.toValueArray(argumentList...))
|
||||
}
|
||||
|
||||
func defaultConstructFunction(self *_object, this Value, argumentList []Value) Value {
|
||||
|
|
|
|||
205
type_go_array.go
Normal file
205
type_go_array.go
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
package otto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object {
|
||||
self := runtime.newObject()
|
||||
self.class = "Array" // TODO Should this be something else?
|
||||
self.stash = newGoArrayStash(value, self.stash)
|
||||
return self
|
||||
}
|
||||
|
||||
type _goArrayStash struct {
|
||||
value reflect.Value
|
||||
writable bool
|
||||
propertyMode _propertyMode
|
||||
_stash
|
||||
}
|
||||
|
||||
func newGoArrayStash(value reflect.Value, stash _stash) *_goArrayStash {
|
||||
propertyMode := _propertyMode(0111)
|
||||
writable := true
|
||||
switch value.Kind() {
|
||||
case reflect.Slice:
|
||||
case reflect.Array:
|
||||
// TODO We need SliceOf to exists (go1.1)
|
||||
// TODO Mark as unwritable for now
|
||||
propertyMode = 0010
|
||||
writable = false
|
||||
default:
|
||||
dbgf("%/panic//%@: %v != reflect.Slice", value.Kind())
|
||||
}
|
||||
self := &_goArrayStash{
|
||||
value: value,
|
||||
writable: writable,
|
||||
propertyMode: propertyMode,
|
||||
_stash: stash,
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func (self _goArrayStash) getValue(index int) (reflect.Value, bool) {
|
||||
if index < self.value.Len() {
|
||||
return self.value.Index(index), true
|
||||
}
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
func (self _goArrayStash) setValue(index int, value Value) {
|
||||
indexValue, exists := self.getValue(index)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
indexValue.Set(reflectValue)
|
||||
}
|
||||
|
||||
// read
|
||||
|
||||
func (self *_goArrayStash) test(name string) bool {
|
||||
// length
|
||||
if name == "length" {
|
||||
return true
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := stringToArrayIndex(name)
|
||||
if index >= 0 {
|
||||
_, exists := self.getValue(int(index))
|
||||
return exists
|
||||
}
|
||||
|
||||
return self._stash.test(name)
|
||||
}
|
||||
|
||||
func (self *_goArrayStash) get(name string) Value {
|
||||
// length
|
||||
if name == "length" {
|
||||
return toValue(self.value.Len())
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := stringToArrayIndex(name)
|
||||
if index >= 0 {
|
||||
value, exists := self.getValue(int(index))
|
||||
if !exists {
|
||||
return UndefinedValue()
|
||||
}
|
||||
return toValue(value)
|
||||
}
|
||||
|
||||
return self._stash.get(name)
|
||||
}
|
||||
|
||||
func (self *_goArrayStash) property(name string) *_property {
|
||||
// length
|
||||
if name == "length" {
|
||||
return &_property{
|
||||
value: toValue(self.value.Len()),
|
||||
mode: 0000, // -Write -Enumerate -Configure
|
||||
// -Write is different from the standard Array
|
||||
}
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := stringToArrayIndex(name)
|
||||
if index >= 0 {
|
||||
value := UndefinedValue()
|
||||
reflectValue, exists := self.getValue(int(index))
|
||||
if exists {
|
||||
value = toValue(reflectValue)
|
||||
}
|
||||
return &_property{
|
||||
value: value,
|
||||
mode: self.propertyMode, // If addressable or not
|
||||
}
|
||||
}
|
||||
|
||||
return self._stash.property(name)
|
||||
}
|
||||
|
||||
func (self *_goArrayStash) enumerate(each func(string)) {
|
||||
// .0, .1, .2, ...
|
||||
|
||||
for index, length := 0, self.value.Len(); index < length; index++ {
|
||||
name := strconv.FormatInt(int64(index), 10)
|
||||
each(name)
|
||||
}
|
||||
|
||||
self._stash.enumerate(each)
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
func (self *_goArrayStash) canPut(name string) bool {
|
||||
// length
|
||||
if name == "length" {
|
||||
return false
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := int(stringToArrayIndex(name))
|
||||
if index >= 0 {
|
||||
if self.writable {
|
||||
length := self.value.Len()
|
||||
if index < length {
|
||||
return self.writable
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return self._stash.canPut(name)
|
||||
}
|
||||
|
||||
func (self *_goArrayStash) put(name string, value Value) {
|
||||
// length
|
||||
if name == "length" {
|
||||
return
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := int(stringToArrayIndex(name))
|
||||
if index >= 0 {
|
||||
if self.writable {
|
||||
length := self.value.Len()
|
||||
if index < length {
|
||||
self.setValue(index, value)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self._stash.put(name, value)
|
||||
}
|
||||
|
||||
func (self *_goArrayStash) delete(name string) {
|
||||
// length
|
||||
if name == "length" {
|
||||
return
|
||||
}
|
||||
|
||||
// .0, .1, .2, ...
|
||||
index := int(stringToArrayIndex(name))
|
||||
if index >= 0 {
|
||||
if self.writable {
|
||||
length := self.value.Len()
|
||||
if index < length {
|
||||
indexValue, exists := self.getValue(index)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
indexValue.Set(reflect.Zero(self.value.Type().Elem()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self._stash.delete(name)
|
||||
}
|
||||
101
type_go_map.go
Normal file
101
type_go_map.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
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.stash = newGoMapStash(value, self.stash)
|
||||
return self
|
||||
}
|
||||
|
||||
type _goMapStash struct {
|
||||
value reflect.Value
|
||||
keyKind reflect.Kind
|
||||
valueKind reflect.Kind
|
||||
_stash
|
||||
}
|
||||
|
||||
func newGoMapStash(value reflect.Value, stash _stash) *_goMapStash {
|
||||
if value.Kind() != reflect.Map {
|
||||
dbgf("%/panic//%@: %v != reflect.Map", value.Kind())
|
||||
}
|
||||
self := &_goMapStash{
|
||||
value: value,
|
||||
keyKind: value.Type().Key().Kind(),
|
||||
valueKind: value.Type().Elem().Kind(),
|
||||
_stash: stash,
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func (self _goMapStash) toKey(name string) reflect.Value {
|
||||
reflectValue, err := stringToReflectValue(name, self.keyKind)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return reflectValue
|
||||
}
|
||||
|
||||
func (self _goMapStash) toValue(value Value) reflect.Value {
|
||||
reflectValue, err := value.toReflectValue(self.valueKind)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return reflectValue
|
||||
}
|
||||
|
||||
// read
|
||||
|
||||
func (self *_goMapStash) test(name string) bool {
|
||||
value := self.value.MapIndex(self.toKey(name))
|
||||
if value.IsValid() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *_goMapStash) get(name string) Value {
|
||||
value := self.value.MapIndex(self.toKey(name))
|
||||
if value.IsValid() {
|
||||
return toValue(value)
|
||||
}
|
||||
|
||||
return UndefinedValue()
|
||||
}
|
||||
|
||||
func (self *_goMapStash) property(name string) *_property {
|
||||
value := self.value.MapIndex(self.toKey(name))
|
||||
if value.IsValid() {
|
||||
return &_property{
|
||||
toValue(value),
|
||||
0111, // +Write +Enumerate +Configure
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *_goMapStash) enumerate(each func(string)) {
|
||||
keys := self.value.MapKeys()
|
||||
for _, key := range keys {
|
||||
each(key.String())
|
||||
}
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
func (self *_goMapStash) canPut(name string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *_goMapStash) put(name string, value Value) {
|
||||
self.value.SetMapIndex(self.toKey(name), self.toValue(value))
|
||||
}
|
||||
|
||||
func (self *_goMapStash) delete(name string) {
|
||||
// Setting a zero Value will delete the key
|
||||
self.value.SetMapIndex(self.toKey(name), reflect.Value{})
|
||||
}
|
||||
118
type_go_struct.go
Normal file
118
type_go_struct.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package otto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object {
|
||||
self := runtime.newObject()
|
||||
self.class = "Object" // TODO Should this be something else?
|
||||
self.stash = newGoStructStash(value, self.stash)
|
||||
return self
|
||||
}
|
||||
|
||||
type _goStructStash struct {
|
||||
value reflect.Value
|
||||
_stash
|
||||
}
|
||||
|
||||
func newGoStructStash(value reflect.Value, stash _stash) *_goStructStash {
|
||||
if value.Kind() != reflect.Struct {
|
||||
dbgf("%/panic//%@: %v != reflect.Struct", value.Kind())
|
||||
}
|
||||
self := &_goStructStash{
|
||||
value: value,
|
||||
_stash: stash,
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func (self _goStructStash) getValue(name string) reflect.Value {
|
||||
return self.value.FieldByName(name)
|
||||
}
|
||||
|
||||
func (self _goStructStash) field(name string) (reflect.StructField, bool) {
|
||||
return self.value.Type().FieldByName(name)
|
||||
}
|
||||
|
||||
func (self _goStructStash) 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
|
||||
}
|
||||
|
||||
// read
|
||||
|
||||
func (self *_goStructStash) test(name string) bool {
|
||||
value := self.getValue(name)
|
||||
if value.IsValid() {
|
||||
return true
|
||||
}
|
||||
return self._stash.test(name)
|
||||
}
|
||||
|
||||
func (self *_goStructStash) get(name string) Value {
|
||||
value := self.getValue(name)
|
||||
if value.IsValid() {
|
||||
return toValue(value)
|
||||
}
|
||||
|
||||
return self._stash.get(name)
|
||||
}
|
||||
|
||||
func (self *_goStructStash) property(name string) *_property {
|
||||
value := self.getValue(name)
|
||||
if value.IsValid() {
|
||||
return &_property{
|
||||
toValue(value),
|
||||
0111, // +Write +Enumerate +Configure
|
||||
}
|
||||
}
|
||||
|
||||
return self._stash.property(name)
|
||||
}
|
||||
|
||||
func (self *_goStructStash) enumerate(each func(string)) {
|
||||
count := self.value.NumField()
|
||||
type_ := self.value.Type()
|
||||
for index := 0; index < count; index++ {
|
||||
each(type_.Field(index).Name)
|
||||
}
|
||||
|
||||
self._stash.enumerate(each)
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
func (self *_goStructStash) canPut(name string) bool {
|
||||
value := self.getValue(name)
|
||||
if value.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
return self._stash.canPut(name)
|
||||
}
|
||||
|
||||
func (self *_goStructStash) put(name string, value Value) {
|
||||
if self.setValue(name, value) {
|
||||
return
|
||||
}
|
||||
|
||||
self._stash.put(name, value)
|
||||
}
|
||||
|
||||
func (self *_goStructStash) delete(name string) {
|
||||
if _, exists := self.field(name); exists {
|
||||
return
|
||||
}
|
||||
|
||||
self._stash.delete(name)
|
||||
}
|
||||
256
value.go
256
value.go
|
|
@ -3,6 +3,8 @@ package otto
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type _valueType int
|
||||
|
|
@ -59,8 +61,6 @@ func (value Value) IsUndefined() bool {
|
|||
return value._valueType == valueUndefined
|
||||
}
|
||||
|
||||
// Any nil will do -- we just make a new throwaway type here
|
||||
|
||||
// NullValue will return a Value representing null.
|
||||
func NullValue() Value {
|
||||
return Value{_valueType: valueNull}
|
||||
|
|
@ -232,6 +232,17 @@ func (value Value) isError() bool {
|
|||
|
||||
// ---
|
||||
|
||||
func toValue_reflectValuePanic(value interface{}, kind reflect.Kind) {
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
panic(newTypeError("Invalid value (struct): Missing runtime: %v (%T)", value, value))
|
||||
case reflect.Map:
|
||||
panic(newTypeError("Invalid value (map): Missing runtime: %v (%T)", value, value))
|
||||
case reflect.Slice:
|
||||
panic(newTypeError("Invalid value (slice): Missing runtime: %v (%T)", value, value))
|
||||
}
|
||||
}
|
||||
|
||||
func toValue(value interface{}) Value {
|
||||
switch value := value.(type) {
|
||||
case Value:
|
||||
|
|
@ -273,8 +284,47 @@ func toValue(value interface{}) Value {
|
|||
return Value{valueObject, value.object}
|
||||
case _reference: // reference is an interface (already a pointer)
|
||||
return Value{valueReference, value}
|
||||
case reflect.Value:
|
||||
value = reflect.Indirect(value)
|
||||
switch value.Kind() {
|
||||
case reflect.Bool:
|
||||
return Value{valueBoolean, bool(value.Bool())}
|
||||
case reflect.Int:
|
||||
return Value{valueNumber, int(value.Int())}
|
||||
case reflect.Int8:
|
||||
return Value{valueNumber, int8(value.Int())}
|
||||
case reflect.Int16:
|
||||
return Value{valueNumber, int16(value.Int())}
|
||||
case reflect.Int32:
|
||||
return Value{valueNumber, int32(value.Int())}
|
||||
case reflect.Int64:
|
||||
return Value{valueNumber, int64(value.Int())}
|
||||
case reflect.Uint:
|
||||
return Value{valueNumber, uint(value.Uint())}
|
||||
case reflect.Uint8:
|
||||
return Value{valueNumber, uint8(value.Uint())}
|
||||
case reflect.Uint16:
|
||||
return Value{valueNumber, uint16(value.Uint())}
|
||||
case reflect.Uint32:
|
||||
return Value{valueNumber, uint32(value.Uint())}
|
||||
case reflect.Uint64:
|
||||
return Value{valueNumber, uint64(value.Uint())}
|
||||
case reflect.Float32:
|
||||
return Value{valueNumber, float32(value.Float())}
|
||||
case reflect.Float64:
|
||||
return Value{valueNumber, float64(value.Float())}
|
||||
case reflect.String:
|
||||
return Value{valueString, string(value.String())}
|
||||
default:
|
||||
toValue_reflectValuePanic(value.Interface(), value.Kind())
|
||||
}
|
||||
default:
|
||||
{
|
||||
value := reflect.Indirect(reflect.ValueOf(value))
|
||||
toValue_reflectValuePanic(value.Interface(), value.Kind())
|
||||
}
|
||||
}
|
||||
panic(newTypeError("Unable to convert value: %v (%T)", value, value))
|
||||
panic(newTypeError("Invalid value: Unsupported: %v (%T)", value, value))
|
||||
}
|
||||
|
||||
// String will return the value as a string.
|
||||
|
|
@ -395,15 +445,13 @@ func (value Value) reference() _reference {
|
|||
return nil
|
||||
}
|
||||
|
||||
var __NaN__, __PositiveInfinity__, __NegativeInfinity__, __PositiveZero__, __NegativeZero__ float64
|
||||
|
||||
func init() {
|
||||
__NaN__ = math.NaN()
|
||||
__PositiveInfinity__ = math.Inf(+1)
|
||||
__NegativeInfinity__ = math.Inf(-1)
|
||||
__PositiveZero__ = 0
|
||||
__NegativeZero__ = math.Float64frombits(0 | (1 << 63))
|
||||
}
|
||||
var (
|
||||
__NaN__ float64 = math.NaN()
|
||||
__PositiveInfinity__ float64 = math.Inf(+1)
|
||||
__NegativeInfinity__ float64 = math.Inf(-1)
|
||||
__PositiveZero__ float64 = 0
|
||||
__NegativeZero__ float64 = math.Float64frombits(0 | (1 << 63))
|
||||
)
|
||||
|
||||
func positiveInfinity() float64 {
|
||||
return __PositiveInfinity__
|
||||
|
|
@ -564,3 +612,187 @@ func (value Value) Export() (interface{}, error) {
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return reflect.ValueOf(value.toBoolean()), nil
|
||||
case reflect.Int:
|
||||
tmp := toIntegerFloat(value)
|
||||
if tmp < _MinInt_ || tmp > _MaxInt_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(int(tmp)), nil
|
||||
}
|
||||
case reflect.Int8:
|
||||
tmp := toInteger(value)
|
||||
if tmp < _MinInt8_ || tmp > _MaxInt8_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int8", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(int8(tmp)), nil
|
||||
}
|
||||
case reflect.Int16:
|
||||
tmp := toInteger(value)
|
||||
if tmp < _MinInt16_ || tmp > _MaxInt16_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int16", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(int16(tmp)), nil
|
||||
}
|
||||
case reflect.Int32:
|
||||
tmp := toInteger(value)
|
||||
if tmp < _MinInt32_ || tmp > _MaxInt32_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int32", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(int32(tmp)), nil
|
||||
}
|
||||
case reflect.Int64:
|
||||
tmp := toIntegerFloat(value)
|
||||
if tmp < _MinInt64_ || tmp > _MaxInt64_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(int64(tmp)), nil
|
||||
}
|
||||
case reflect.Uint:
|
||||
tmp := toIntegerFloat(value)
|
||||
if tmp < 0 || tmp > _MaxUint_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(uint(tmp)), nil
|
||||
}
|
||||
case reflect.Uint8:
|
||||
tmp := toInteger(value)
|
||||
if tmp < 0 || tmp > _MaxUint8_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint8", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(uint8(tmp)), nil
|
||||
}
|
||||
case reflect.Uint16:
|
||||
tmp := toInteger(value)
|
||||
if tmp < 0 || tmp > _MaxUint16_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint16", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(uint16(tmp)), nil
|
||||
}
|
||||
case reflect.Uint32:
|
||||
tmp := toInteger(value)
|
||||
if tmp < 0 || tmp > _MaxUint32_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint32", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(uint32(tmp)), nil
|
||||
}
|
||||
case reflect.Uint64:
|
||||
tmp := toIntegerFloat(value)
|
||||
if tmp < 0 || tmp > _MaxUint64_ {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint64", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(uint64(tmp)), nil
|
||||
}
|
||||
case reflect.Float32:
|
||||
tmp := toFloat(value)
|
||||
tmp1 := tmp
|
||||
if 0 > tmp1 {
|
||||
tmp1 = -tmp1
|
||||
}
|
||||
if tmp1 < math.SmallestNonzeroFloat32 || tmp1 > math.MaxFloat32 {
|
||||
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to float32", tmp, value)
|
||||
} else {
|
||||
return reflect.ValueOf(float32(tmp)), nil
|
||||
}
|
||||
case reflect.Float64:
|
||||
value := toFloat(value)
|
||||
return reflect.ValueOf(float64(value)), nil
|
||||
case reflect.String:
|
||||
return reflect.ValueOf(value.toString()), nil
|
||||
}
|
||||
|
||||
dbgf("%/panic//%@: Invalid: (%v) to reflect.Kind: %v", value, kind)
|
||||
panic("")
|
||||
}
|
||||
|
||||
func stringToReflectValue(value string, kind reflect.Kind) (reflect.Value, error) {
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
value, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(value), nil
|
||||
case reflect.Int:
|
||||
value, err := strconv.ParseInt(value, 0, 0)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(int(value)), nil
|
||||
case reflect.Int8:
|
||||
value, err := strconv.ParseInt(value, 0, 8)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(int8(value)), nil
|
||||
case reflect.Int16:
|
||||
value, err := strconv.ParseInt(value, 0, 16)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(int16(value)), nil
|
||||
case reflect.Int32:
|
||||
value, err := strconv.ParseInt(value, 0, 32)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(int32(value)), nil
|
||||
case reflect.Int64:
|
||||
value, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(int64(value)), nil
|
||||
case reflect.Uint:
|
||||
value, err := strconv.ParseUint(value, 0, 0)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(uint(value)), nil
|
||||
case reflect.Uint8:
|
||||
value, err := strconv.ParseUint(value, 0, 8)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(uint8(value)), nil
|
||||
case reflect.Uint16:
|
||||
value, err := strconv.ParseUint(value, 0, 16)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(uint16(value)), nil
|
||||
case reflect.Uint32:
|
||||
value, err := strconv.ParseUint(value, 0, 32)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(uint32(value)), nil
|
||||
case reflect.Uint64:
|
||||
value, err := strconv.ParseUint(value, 0, 64)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(uint64(value)), nil
|
||||
case reflect.Float32:
|
||||
value, err := strconv.ParseFloat(value, 32)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(float32(value)), nil
|
||||
case reflect.Float64:
|
||||
value, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return reflect.ValueOf(float64(value)), nil
|
||||
case reflect.String:
|
||||
return reflect.ValueOf(value), nil
|
||||
}
|
||||
|
||||
dbgf("%/panic//%@: Invalid: \"%s\" to reflect.Kind: %v", value, kind)
|
||||
panic("")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,6 +100,46 @@ const (
|
|||
sqrt1_2 float64 = math.Sqrt2 / 2
|
||||
)
|
||||
|
||||
const (
|
||||
_MaxInt8 = math.MaxInt8
|
||||
_MinInt8 = math.MinInt8
|
||||
_MaxInt16 = math.MaxInt16
|
||||
_MinInt16 = math.MinInt16
|
||||
_MaxInt32 = math.MaxInt32
|
||||
_MinInt32 = math.MinInt32
|
||||
_MaxInt64 = math.MaxInt64
|
||||
_MinInt64 = math.MinInt64
|
||||
_MaxUint8 = math.MaxUint8
|
||||
_MaxUint16 = math.MaxUint16
|
||||
_MaxUint32 = math.MaxUint32
|
||||
_MaxUint64 = math.MaxUint64
|
||||
_MaxUint = ^uint(0)
|
||||
_MinUint = 0
|
||||
_MaxInt = int(^uint(0) >> 1)
|
||||
_MinInt = -_MaxInt - 1
|
||||
|
||||
// int64
|
||||
_MaxInt8_ int64 = math.MaxInt8
|
||||
_MinInt8_ int64 = math.MinInt8
|
||||
_MaxInt16_ int64 = math.MaxInt16
|
||||
_MinInt16_ int64 = math.MinInt16
|
||||
_MaxInt32_ int64 = math.MaxInt32
|
||||
_MinInt32_ int64 = math.MinInt32
|
||||
_MaxUint8_ int64 = math.MaxUint8
|
||||
_MaxUint16_ int64 = math.MaxUint16
|
||||
_MaxUint32_ int64 = math.MaxUint32
|
||||
|
||||
// float64
|
||||
_MaxInt_ float64 = float64(int(^uint(0) >> 1))
|
||||
_MinInt_ float64 = float64(int(-_MaxInt - 1))
|
||||
_MinUint_ float64 = float64(0)
|
||||
_MaxUint_ float64 = float64(uint(^uint(0)))
|
||||
_MinUint64_ float64 = float64(0)
|
||||
_MaxUint64_ float64 = math.MaxUint64
|
||||
_MaxInt64_ float64 = math.MaxInt64
|
||||
_MinInt64_ float64 = math.MinInt64
|
||||
)
|
||||
|
||||
func toIntegerFloat(value Value) float64 {
|
||||
floatValue := value.toFloat()
|
||||
if math.IsNaN(floatValue) {
|
||||
|
|
@ -132,7 +172,8 @@ func toInteger(value Value) int64 {
|
|||
if math.IsNaN(floatValue) {
|
||||
return 0
|
||||
} else if math.IsInf(floatValue, 0) {
|
||||
panic(hereBeDragons())
|
||||
// FIXME
|
||||
dbgf("%/panic//%@: %f %v to int64", floatValue, value)
|
||||
}
|
||||
if floatValue > 0 {
|
||||
return int64(math.Floor(floatValue))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user