289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
712 lines
19 KiB
Go
712 lines
19 KiB
Go
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
|
|
}
|