package otto import ( "fmt" "math" "reflect" "strconv" "unicode/utf16" ) type _valueKind int const ( valueUndefined _valueKind = iota valueNull valueNumber valueString valueBoolean valueObject // These are invalid outside of the runtime valueEmpty valueResult valueReference ) // Value is the representation of a JavaScript value. type Value struct { kind _valueKind value interface{} } func (vl Value) safe() bool { return vl.kind < valueEmpty } var ( emptyValue = Value{kind: valueEmpty} nullValue = Value{kind: valueNull} falseValue = Value{kind: valueBoolean, value: false} trueValue = Value{kind: valueBoolean, value: true} ) // 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 ToValue(value interface{}) (Value, error) { result := Value{} err := catchPanic(func() { result = toValue(value) }) return result, err } func (value Value) isEmpty() bool { return value.kind == valueEmpty } // Undefined // UndefinedValue will return a Value representing undefined. func UndefinedValue() Value { return Value{} } // IsDefined will return false if the value is undefined, and true otherwise. func (value Value) IsDefined() bool { return value.kind != valueUndefined } // IsUndefined will return true if the value is undefined, and false otherwise. func (value Value) IsUndefined() bool { return value.kind == valueUndefined } // NullValue will return a Value representing null. func NullValue() Value { return Value{kind: valueNull} } // IsNull will return true if the value is null, and false otherwise. func (value Value) IsNull() bool { return value.kind == valueNull } // --- func (value Value) isCallable() bool { switch value := value.value.(type) { case *_object: return value.isCall() } return false } // 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 Value) Call(this Value, argumentList ...interface{}) (Value, error) { result := Value{} err := catchPanic(func() { // FIXME result = value.call(nil, this, argumentList...) }) if !value.safe() { value = Value{} } return result, err } func (value Value) call(rt *_runtime, this Value, argumentList ...interface{}) Value { switch function := value.value.(type) { case *_object: return function.call(this, function.runtime.toValueArray(argumentList...), false, nativeFrame) } if rt == nil { panic("FIXME TypeError") } panic(rt.panicTypeError()) } func (value Value) constructSafe(rt *_runtime, this Value, argumentList ...interface{}) (Value, error) { result := Value{} err := catchPanic(func() { result = value.construct(rt, this, argumentList...) }) return result, err } func (value Value) construct(rt *_runtime, this Value, argumentList ...interface{}) Value { switch fn := value.value.(type) { case *_object: return fn.construct(fn.runtime.toValueArray(argumentList...)) } if rt == nil { panic("FIXME TypeError") } panic(rt.panicTypeError()) } // IsPrimitive will return true if value is a primitive (any kind of primitive). func (value Value) IsPrimitive() bool { return !value.IsObject() } // IsBoolean will return true if value is a boolean (primitive). func (value Value) IsBoolean() bool { return value.kind == valueBoolean } // IsNumber will return true if value is a number (primitive). func (value Value) IsNumber() bool { return value.kind == valueNumber } // IsNaN will return true if value is NaN (or would convert to NaN). func (value Value) IsNaN() bool { switch value := value.value.(type) { case float64: return math.IsNaN(value) case float32: return math.IsNaN(float64(value)) case int, int8, int32, int64: return false case uint, uint8, uint32, uint64: return false } return math.IsNaN(value.float64()) } // IsString will return true if value is a string (primitive). func (value Value) IsString() bool { return value.kind == valueString } // IsObject will return true if value is an object. func (value Value) IsObject() bool { return value.kind == valueObject } // IsFunction will return true if value is a function. func (value Value) IsFunction() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "Function" } // 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 Value) Class() string { if value.kind != valueObject { return "" } return value.value.(*_object).class } func (value Value) isArray() bool { if value.kind != valueObject { return false } return isArray(value.value.(*_object)) } func (value Value) isStringObject() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "String" } func (value Value) isBooleanObject() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "Boolean" } func (value Value) isNumberObject() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "Number" } func (value Value) isDate() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "Date" } func (value Value) isRegExp() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "RegExp" } func (value Value) isError() bool { if value.kind != valueObject { return false } return value.value.(*_object).class == "Error" } // --- func toValue_reflectValuePanic(value interface{}, kind reflect.Kind) { // FIXME? switch kind { case reflect.Struct: panic(newError(nil, "TypeError", "invalid value (struct): missing runtime: %v (%T)", value, value)) case reflect.Map: panic(newError(nil, "TypeError", "invalid value (map): missing runtime: %v (%T)", value, value)) case reflect.Slice: panic(newError(nil, "TypeError", "invalid value (slice): missing runtime: %v (%T)", value, value)) } } func toValue(value interface{}) Value { switch value := value.(type) { case Value: return value case bool: return Value{valueBoolean, value} case int: return Value{valueNumber, value} case int8: return Value{valueNumber, value} case int16: return Value{valueNumber, value} case int32: return Value{valueNumber, value} case int64: return Value{valueNumber, value} case uint: return Value{valueNumber, value} case uint8: return Value{valueNumber, value} case uint16: return Value{valueNumber, value} case uint32: return Value{valueNumber, value} case uint64: return Value{valueNumber, value} case float32: return Value{valueNumber, float64(value)} case float64: return Value{valueNumber, value} case []uint16: return Value{valueString, value} case string: return Value{valueString, value} // A rune is actually an int32, which is handled above case *_object: return Value{valueObject, value} case *Object: return Value{valueObject, value.object} case Object: return Value{valueObject, value.object} case _reference: // reference is an interface (already a pointer) return Value{valueReference, value} case _result: return Value{valueResult, value} case nil: // TODO Ugh. return Value{} case reflect.Value: for value.Kind() == reflect.Ptr { // We were given a pointer, so we'll drill down until we get a non-pointer // // These semantics might change if we want to start supporting pointers to values transparently // (It would be best not to depend on this behavior) // FIXME: UNDEFINED if value.IsNil() { return Value{} } value = value.Elem() } 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: return toValue(reflect.ValueOf(value)) } // FIXME? panic(newError(nil, "TypeError", "invalid value: %v (%T)", value, value)) } // String will return the value as a string. // // This method will make return the empty string if there is an error. func (value Value) String() string { result := "" catchPanic(func() { result = value.string() }) return result } // 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 Value) ToBoolean() (bool, error) { result := false err := catchPanic(func() { result = value.bool() }) return result, err } func (value Value) numberValue() Value { if value.kind == valueNumber { return value } return Value{valueNumber, value.float64()} } // 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 Value) ToFloat() (float64, error) { result := float64(0) err := catchPanic(func() { result = value.float64() }) return result, err } // 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 Value) ToInteger() (int64, error) { result := int64(0) err := catchPanic(func() { result = value.number().int64 }) return result, err } // 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. func (value Value) ToString() (string, error) { result := "" err := catchPanic(func() { result = value.string() }) return result, err } func (value Value) _object() *_object { switch value := value.value.(type) { case *_object: return value } return nil } // 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 Value) Object() *Object { switch object := value.value.(type) { case *_object: return _newObject(object, value) } return nil } func (value Value) reference() _reference { switch value := value.value.(type) { case _reference: return value } return nil } func (value Value) resolve() Value { switch value := value.value.(type) { case _reference: return value.getValue() } return value } 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__ } func negativeInfinity() float64 { return __NegativeInfinity__ } func positiveZero() float64 { return __PositiveZero__ } func negativeZero() float64 { return __NegativeZero__ } // NaNValue will return a value representing NaN. // // It is equivalent to: // // ToValue(math.NaN()) // func NaNValue() Value { return Value{valueNumber, __NaN__} } func positiveInfinityValue() Value { return Value{valueNumber, __PositiveInfinity__} } func negativeInfinityValue() Value { return Value{valueNumber, __NegativeInfinity__} } func positiveZeroValue() Value { return Value{valueNumber, __PositiveZero__} } func negativeZeroValue() Value { return Value{valueNumber, __NegativeZero__} } // TrueValue will return a value representing true. // // It is equivalent to: // // ToValue(true) // func TrueValue() Value { return Value{valueBoolean, true} } // FalseValue will return a value representing false. // // It is equivalent to: // // ToValue(false) // func FalseValue() Value { return Value{valueBoolean, false} } func sameValue(x Value, y Value) bool { if x.kind != y.kind { return false } result := false switch x.kind { case valueUndefined, valueNull: result = true case valueNumber: x := x.float64() y := y.float64() if math.IsNaN(x) && math.IsNaN(y) { result = true } else { result = x == y if result && x == 0 { // Since +0 != -0 result = math.Signbit(x) == math.Signbit(y) } } case valueString: result = x.string() == y.string() case valueBoolean: result = x.bool() == y.bool() case valueObject: result = x._object() == y._object() default: panic(hereBeDragons()) } return result } func strictEqualityComparison(x Value, y Value) bool { if x.kind != y.kind { return false } result := false 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: panic(hereBeDragons()) } return result } // 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 (self Value) Export() (interface{}, error) { return self.export(), nil } func (self Value) export() interface{} { switch self.kind { case valueUndefined: return nil case valueNull: return nil case valueNumber, valueBoolean: return self.value case valueString: switch value := self.value.(type) { case string: return value case []uint16: return string(utf16.Decode(value)) } case valueObject: object := self._object() switch value := object.value.(type) { case *_goStructObject: return value.value.Interface() case *_goMapObject: return value.value.Interface() case *_goArrayObject: return value.value.Interface() case *_goSliceObject: return value.value.Interface() } if object.class == "Array" { result := make([]interface{}, 0) lengthValue := object.get("length") length := lengthValue.value.(uint32) for index := uint32(0); index < length; index += 1 { name := strconv.FormatInt(int64(index), 10) if !object.hasProperty(name) { continue } value := object.get(name) result = append(result, value.export()) } return result } else { result := make(map[string]interface{}) // TODO Should we export everything? Or just what is enumerable? object.enumerate(false, func(name string) bool { value := object.get(name) if value.IsDefined() { result[name] = value.export() } return true }) return result } } if self.safe() { return self } return Value{} } func (self Value) evaluateBreakContinue(labels []string) _resultKind { result := self.value.(_result) if result.kind == resultBreak || result.kind == resultContinue { for _, label := range labels { if label == result.target { return result.kind } } } return resultReturn } func (self Value) evaluateBreak(labels []string) _resultKind { result := self.value.(_result) if result.kind == resultBreak { for _, label := range labels { if label == result.target { return result.kind } } } return resultReturn } func (self Value) exportNative() interface{} { switch self.kind { case valueUndefined: return self case valueNull: return nil case valueNumber, valueBoolean: return self.value case valueString: switch value := self.value.(type) { case string: return value case []uint16: return string(utf16.Decode(value)) } case valueObject: object := self._object() switch value := object.value.(type) { case *_goStructObject: return value.value.Interface() case *_goMapObject: return value.value.Interface() case *_goArrayObject: return value.value.Interface() case *_goSliceObject: return value.value.Interface() } } return self } // Make a best effort to return a reflect.Value corresponding to reflect.Kind, but // fallback to just returning the Go value we have handy. func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) { switch kind { case reflect.Bool: // Bool return reflect.ValueOf(value.bool()), nil case reflect.Int: // Int // We convert to float64 here because converting to int64 will not tell us // if a value is outside the range of int64 tmp := toIntegerFloat(value) if tmp < float_minInt || tmp > float_maxInt { return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value) } else { return reflect.ValueOf(int(tmp)), nil } case reflect.Int8: // Int8 tmp := value.number().int64 if tmp < int64_minInt8 || tmp > int64_maxInt8 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int8", tmp, value) } else { return reflect.ValueOf(int8(tmp)), nil } case reflect.Int16: // Int16 tmp := value.number().int64 if tmp < int64_minInt16 || tmp > int64_maxInt16 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int16", tmp, value) } else { return reflect.ValueOf(int16(tmp)), nil } case reflect.Int32: // Int32 tmp := value.number().int64 if tmp < int64_minInt32 || tmp > int64_maxInt32 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int32", tmp, value) } else { return reflect.ValueOf(int32(tmp)), nil } case reflect.Int64: // Int64 // We convert to float64 here because converting to int64 will not tell us // if a value is outside the range of int64 tmp := toIntegerFloat(value) if tmp < float_minInt64 || tmp > float_maxInt64 { return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value) } else { return reflect.ValueOf(int64(tmp)), nil } case reflect.Uint: // Uint // We convert to float64 here because converting to int64 will not tell us // if a value is outside the range of uint tmp := toIntegerFloat(value) if tmp < 0 || tmp > float_maxUint { return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint", tmp, value) } else { return reflect.ValueOf(uint(tmp)), nil } case reflect.Uint8: // Uint8 tmp := value.number().int64 if tmp < 0 || tmp > int64_maxUint8 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint8", tmp, value) } else { return reflect.ValueOf(uint8(tmp)), nil } case reflect.Uint16: // Uint16 tmp := value.number().int64 if tmp < 0 || tmp > int64_maxUint16 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint16", tmp, value) } else { return reflect.ValueOf(uint16(tmp)), nil } case reflect.Uint32: // Uint32 tmp := value.number().int64 if tmp < 0 || tmp > int64_maxUint32 { return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint32", tmp, value) } else { return reflect.ValueOf(uint32(tmp)), nil } case reflect.Uint64: // Uint64 // We convert to float64 here because converting to int64 will not tell us // if a value is outside the range of uint64 tmp := toIntegerFloat(value) if tmp < 0 || tmp > float_maxUint64 { return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint64", tmp, value) } else { return reflect.ValueOf(uint64(tmp)), nil } case reflect.Float32: // Float32 tmp := value.float64() 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: // Float64 value := value.float64() return reflect.ValueOf(float64(value)), nil case reflect.String: // String return reflect.ValueOf(value.string()), nil case reflect.Invalid: // Invalid case reflect.Complex64: // FIXME? Complex64 case reflect.Complex128: // FIXME? Complex128 case reflect.Chan: // FIXME? Chan case reflect.Func: // FIXME? Func case reflect.Ptr: // FIXME? Ptr case reflect.UnsafePointer: // FIXME? UnsafePointer default: switch value.kind { case valueObject: object := value._object() switch vl := object.value.(type) { case *_goStructObject: // Struct return reflect.ValueOf(vl.value.Interface()), nil case *_goMapObject: // Map return reflect.ValueOf(vl.value.Interface()), nil case *_goArrayObject: // Array return reflect.ValueOf(vl.value.Interface()), nil case *_goSliceObject: // Slice return reflect.ValueOf(vl.value.Interface()), nil } return reflect.ValueOf(value.exportNative()), nil case valueEmpty, valueResult, valueReference: // These are invalid, and should panic default: return reflect.ValueOf(value.value), nil } } // FIXME Should this end up as a TypeError? panic(fmt.Errorf("invalid conversion of %v (%v) to reflect.Kind: %v", value.kind, value, kind)) } 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 } // FIXME This should end up as a TypeError? panic(fmt.Errorf("invalid conversion of %q to reflect.Kind: %v", value, kind)) }