evaluate.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package otto
  2. import (
  3. "fmt"
  4. "math"
  5. "strings"
  6. "github.com/robertkrimen/otto/token"
  7. )
  8. func (rt *runtime) evaluateMultiply(left float64, right float64) Value { //nolint:unused
  9. // TODO 11.5.1
  10. return Value{}
  11. }
  12. func (rt *runtime) evaluateDivide(left float64, right float64) Value {
  13. if math.IsNaN(left) || math.IsNaN(right) {
  14. return NaNValue()
  15. }
  16. if math.IsInf(left, 0) && math.IsInf(right, 0) {
  17. return NaNValue()
  18. }
  19. if left == 0 && right == 0 {
  20. return NaNValue()
  21. }
  22. if math.IsInf(left, 0) {
  23. if math.Signbit(left) == math.Signbit(right) {
  24. return positiveInfinityValue()
  25. }
  26. return negativeInfinityValue()
  27. }
  28. if math.IsInf(right, 0) {
  29. if math.Signbit(left) == math.Signbit(right) {
  30. return positiveZeroValue()
  31. }
  32. return negativeZeroValue()
  33. }
  34. if right == 0 {
  35. if math.Signbit(left) == math.Signbit(right) {
  36. return positiveInfinityValue()
  37. }
  38. return negativeInfinityValue()
  39. }
  40. return float64Value(left / right)
  41. }
  42. func (rt *runtime) evaluateModulo(left float64, right float64) Value { //nolint:unused
  43. // TODO 11.5.3
  44. return Value{}
  45. }
  46. func (rt *runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value {
  47. leftValue := left.resolve()
  48. switch operator {
  49. // Additive
  50. case token.PLUS:
  51. leftValue = toPrimitiveValue(leftValue)
  52. rightValue := right.resolve()
  53. rightValue = toPrimitiveValue(rightValue)
  54. if leftValue.IsString() || rightValue.IsString() {
  55. return stringValue(strings.Join([]string{leftValue.string(), rightValue.string()}, ""))
  56. }
  57. return float64Value(leftValue.float64() + rightValue.float64())
  58. case token.MINUS:
  59. rightValue := right.resolve()
  60. return float64Value(leftValue.float64() - rightValue.float64())
  61. // Multiplicative
  62. case token.MULTIPLY:
  63. rightValue := right.resolve()
  64. return float64Value(leftValue.float64() * rightValue.float64())
  65. case token.SLASH:
  66. rightValue := right.resolve()
  67. return rt.evaluateDivide(leftValue.float64(), rightValue.float64())
  68. case token.REMAINDER:
  69. rightValue := right.resolve()
  70. return float64Value(math.Mod(leftValue.float64(), rightValue.float64()))
  71. // Logical
  72. case token.LOGICAL_AND:
  73. left := leftValue.bool()
  74. if !left {
  75. return falseValue
  76. }
  77. return boolValue(right.resolve().bool())
  78. case token.LOGICAL_OR:
  79. left := leftValue.bool()
  80. if left {
  81. return trueValue
  82. }
  83. return boolValue(right.resolve().bool())
  84. // Bitwise
  85. case token.AND:
  86. rightValue := right.resolve()
  87. return int32Value(toInt32(leftValue) & toInt32(rightValue))
  88. case token.OR:
  89. rightValue := right.resolve()
  90. return int32Value(toInt32(leftValue) | toInt32(rightValue))
  91. case token.EXCLUSIVE_OR:
  92. rightValue := right.resolve()
  93. return int32Value(toInt32(leftValue) ^ toInt32(rightValue))
  94. // Shift
  95. // (Masking of 0x1f is to restrict the shift to a maximum of 31 places)
  96. case token.SHIFT_LEFT:
  97. rightValue := right.resolve()
  98. return int32Value(toInt32(leftValue) << (toUint32(rightValue) & 0x1f))
  99. case token.SHIFT_RIGHT:
  100. rightValue := right.resolve()
  101. return int32Value(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f))
  102. case token.UNSIGNED_SHIFT_RIGHT:
  103. rightValue := right.resolve()
  104. // Shifting an unsigned integer is a logical shift
  105. return uint32Value(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f))
  106. case token.INSTANCEOF:
  107. rightValue := right.resolve()
  108. if !rightValue.IsObject() {
  109. panic(rt.panicTypeError("invalid kind %s for instanceof (expected object)", rightValue.kind))
  110. }
  111. return boolValue(rightValue.object().hasInstance(leftValue))
  112. case token.IN:
  113. rightValue := right.resolve()
  114. if !rightValue.IsObject() {
  115. panic(rt.panicTypeError("invalid kind %s for in (expected object)", rightValue.kind))
  116. }
  117. return boolValue(rightValue.object().hasProperty(leftValue.string()))
  118. }
  119. panic(hereBeDragons(operator))
  120. }
  121. type lessThanResult int
  122. const (
  123. lessThanFalse lessThanResult = iota
  124. lessThanTrue
  125. lessThanUndefined
  126. )
  127. func calculateLessThan(left Value, right Value, leftFirst bool) lessThanResult {
  128. var x, y Value
  129. if leftFirst {
  130. x = toNumberPrimitive(left)
  131. y = toNumberPrimitive(right)
  132. } else {
  133. y = toNumberPrimitive(right)
  134. x = toNumberPrimitive(left)
  135. }
  136. var result bool
  137. if x.kind != valueString || y.kind != valueString {
  138. x, y := x.float64(), y.float64()
  139. if math.IsNaN(x) || math.IsNaN(y) {
  140. return lessThanUndefined
  141. }
  142. result = x < y
  143. } else {
  144. x, y := x.string(), y.string()
  145. result = x < y
  146. }
  147. if result {
  148. return lessThanTrue
  149. }
  150. return lessThanFalse
  151. }
  152. // FIXME Probably a map is not the most efficient way to do this.
  153. var lessThanTable [4](map[lessThanResult]bool) = [4](map[lessThanResult]bool){
  154. // <
  155. map[lessThanResult]bool{
  156. lessThanFalse: false,
  157. lessThanTrue: true,
  158. lessThanUndefined: false,
  159. },
  160. // >
  161. map[lessThanResult]bool{
  162. lessThanFalse: false,
  163. lessThanTrue: true,
  164. lessThanUndefined: false,
  165. },
  166. // <=
  167. map[lessThanResult]bool{
  168. lessThanFalse: true,
  169. lessThanTrue: false,
  170. lessThanUndefined: false,
  171. },
  172. // >=
  173. map[lessThanResult]bool{
  174. lessThanFalse: true,
  175. lessThanTrue: false,
  176. lessThanUndefined: false,
  177. },
  178. }
  179. func (rt *runtime) calculateComparison(comparator token.Token, left Value, right Value) bool {
  180. // FIXME Use strictEqualityComparison?
  181. // TODO This might be redundant now (with regards to evaluateComparison)
  182. x := left.resolve()
  183. y := right.resolve()
  184. var kindEqualKind bool
  185. var negate bool
  186. result := true
  187. switch comparator {
  188. case token.LESS:
  189. result = lessThanTable[0][calculateLessThan(x, y, true)]
  190. case token.GREATER:
  191. result = lessThanTable[1][calculateLessThan(y, x, false)]
  192. case token.LESS_OR_EQUAL:
  193. result = lessThanTable[2][calculateLessThan(y, x, false)]
  194. case token.GREATER_OR_EQUAL:
  195. result = lessThanTable[3][calculateLessThan(x, y, true)]
  196. case token.STRICT_NOT_EQUAL:
  197. negate = true
  198. fallthrough
  199. case token.STRICT_EQUAL:
  200. if x.kind != y.kind {
  201. result = false
  202. } else {
  203. kindEqualKind = true
  204. }
  205. case token.NOT_EQUAL:
  206. negate = true
  207. fallthrough
  208. case token.EQUAL:
  209. switch {
  210. case x.kind == y.kind:
  211. kindEqualKind = true
  212. case x.kind <= valueNull && y.kind <= valueNull:
  213. result = true
  214. case x.kind <= valueNull || y.kind <= valueNull:
  215. result = false
  216. case x.kind <= valueString && y.kind <= valueString:
  217. result = x.float64() == y.float64()
  218. case x.kind == valueBoolean:
  219. result = rt.calculateComparison(token.EQUAL, float64Value(x.float64()), y)
  220. case y.kind == valueBoolean:
  221. result = rt.calculateComparison(token.EQUAL, x, float64Value(y.float64()))
  222. case x.kind == valueObject:
  223. result = rt.calculateComparison(token.EQUAL, toPrimitiveValue(x), y)
  224. case y.kind == valueObject:
  225. result = rt.calculateComparison(token.EQUAL, x, toPrimitiveValue(y))
  226. default:
  227. panic(fmt.Sprintf("unknown types for equal: %v ==? %v", x, y))
  228. }
  229. default:
  230. panic("unknown comparator " + comparator.String())
  231. }
  232. if kindEqualKind {
  233. switch x.kind {
  234. case valueUndefined, valueNull:
  235. result = true
  236. case valueNumber:
  237. x := x.float64()
  238. y := y.float64()
  239. if math.IsNaN(x) || math.IsNaN(y) {
  240. result = false
  241. } else {
  242. result = x == y
  243. }
  244. case valueString:
  245. result = x.string() == y.string()
  246. case valueBoolean:
  247. result = x.bool() == y.bool()
  248. case valueObject:
  249. result = x.object() == y.object()
  250. default:
  251. goto ERROR
  252. }
  253. }
  254. if negate {
  255. result = !result
  256. }
  257. return result
  258. ERROR:
  259. panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind))
  260. }