builtin_number.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package goja
  2. import (
  3. "math"
  4. "sync"
  5. "github.com/dop251/goja/ftoa"
  6. )
  7. func (r *Runtime) toNumber(v Value) Value {
  8. switch t := v.(type) {
  9. case valueFloat, valueInt:
  10. return v
  11. case *Object:
  12. switch t := t.self.(type) {
  13. case *primitiveValueObject:
  14. return r.toNumber(t.pValue)
  15. case *objectGoReflect:
  16. if t.class == classNumber && t.valueOf != nil {
  17. return t.valueOf()
  18. }
  19. }
  20. if t == r.global.NumberPrototype {
  21. return _positiveZero
  22. }
  23. }
  24. panic(r.NewTypeError("Value is not a number: %s", v))
  25. }
  26. func (r *Runtime) numberproto_valueOf(call FunctionCall) Value {
  27. return r.toNumber(call.This)
  28. }
  29. func (r *Runtime) numberproto_toString(call FunctionCall) Value {
  30. var numVal Value
  31. switch t := call.This.(type) {
  32. case valueFloat, valueInt:
  33. numVal = t
  34. case *Object:
  35. switch t := t.self.(type) {
  36. case *primitiveValueObject:
  37. numVal = r.toNumber(t.pValue)
  38. case *objectGoReflect:
  39. if t.class == classNumber {
  40. if t.toString != nil {
  41. return t.toString()
  42. }
  43. if t.valueOf != nil {
  44. numVal = t.valueOf()
  45. }
  46. }
  47. }
  48. if t == r.global.NumberPrototype {
  49. return asciiString("0")
  50. }
  51. }
  52. if numVal == nil {
  53. panic(r.NewTypeError("Value is not a number"))
  54. }
  55. var radix int
  56. if arg := call.Argument(0); arg != _undefined {
  57. radix = int(arg.ToInteger())
  58. } else {
  59. radix = 10
  60. }
  61. if radix < 2 || radix > 36 {
  62. panic(r.newError(r.getRangeError(), "toString() radix argument must be between 2 and 36"))
  63. }
  64. num := numVal.ToFloat()
  65. if math.IsNaN(num) {
  66. return stringNaN
  67. }
  68. if math.IsInf(num, 1) {
  69. return stringInfinity
  70. }
  71. if math.IsInf(num, -1) {
  72. return stringNegInfinity
  73. }
  74. if radix == 10 {
  75. return asciiString(fToStr(num, ftoa.ModeStandard, 0))
  76. }
  77. return asciiString(ftoa.FToBaseStr(num, radix))
  78. }
  79. func (r *Runtime) numberproto_toFixed(call FunctionCall) Value {
  80. num := r.toNumber(call.This).ToFloat()
  81. prec := call.Argument(0).ToInteger()
  82. if prec < 0 || prec > 100 {
  83. panic(r.newError(r.getRangeError(), "toFixed() precision must be between 0 and 100"))
  84. }
  85. if math.IsNaN(num) {
  86. return stringNaN
  87. }
  88. return asciiString(fToStr(num, ftoa.ModeFixed, int(prec)))
  89. }
  90. func (r *Runtime) numberproto_toExponential(call FunctionCall) Value {
  91. num := r.toNumber(call.This).ToFloat()
  92. precVal := call.Argument(0)
  93. var prec int64
  94. if precVal == _undefined {
  95. return asciiString(fToStr(num, ftoa.ModeStandardExponential, 0))
  96. } else {
  97. prec = precVal.ToInteger()
  98. }
  99. if math.IsNaN(num) {
  100. return stringNaN
  101. }
  102. if math.IsInf(num, 1) {
  103. return stringInfinity
  104. }
  105. if math.IsInf(num, -1) {
  106. return stringNegInfinity
  107. }
  108. if prec < 0 || prec > 100 {
  109. panic(r.newError(r.getRangeError(), "toExponential() precision must be between 0 and 100"))
  110. }
  111. return asciiString(fToStr(num, ftoa.ModeExponential, int(prec+1)))
  112. }
  113. func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value {
  114. numVal := r.toNumber(call.This)
  115. precVal := call.Argument(0)
  116. if precVal == _undefined {
  117. return numVal.toString()
  118. }
  119. num := numVal.ToFloat()
  120. prec := precVal.ToInteger()
  121. if math.IsNaN(num) {
  122. return stringNaN
  123. }
  124. if math.IsInf(num, 1) {
  125. return stringInfinity
  126. }
  127. if math.IsInf(num, -1) {
  128. return stringNegInfinity
  129. }
  130. if prec < 1 || prec > 100 {
  131. panic(r.newError(r.getRangeError(), "toPrecision() precision must be between 1 and 100"))
  132. }
  133. return asciiString(fToStr(num, ftoa.ModePrecision, int(prec)))
  134. }
  135. func (r *Runtime) number_isFinite(call FunctionCall) Value {
  136. switch arg := call.Argument(0).(type) {
  137. case valueInt:
  138. return valueTrue
  139. case valueFloat:
  140. f := float64(arg)
  141. return r.toBoolean(!math.IsInf(f, 0) && !math.IsNaN(f))
  142. default:
  143. return valueFalse
  144. }
  145. }
  146. func (r *Runtime) number_isInteger(call FunctionCall) Value {
  147. switch arg := call.Argument(0).(type) {
  148. case valueInt:
  149. return valueTrue
  150. case valueFloat:
  151. f := float64(arg)
  152. return r.toBoolean(!math.IsNaN(f) && !math.IsInf(f, 0) && math.Floor(f) == f)
  153. default:
  154. return valueFalse
  155. }
  156. }
  157. func (r *Runtime) number_isNaN(call FunctionCall) Value {
  158. if f, ok := call.Argument(0).(valueFloat); ok && math.IsNaN(float64(f)) {
  159. return valueTrue
  160. }
  161. return valueFalse
  162. }
  163. func (r *Runtime) number_isSafeInteger(call FunctionCall) Value {
  164. arg := call.Argument(0)
  165. if i, ok := arg.(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
  166. return valueTrue
  167. }
  168. if arg == _negativeZero {
  169. return valueTrue
  170. }
  171. return valueFalse
  172. }
  173. func createNumberProtoTemplate() *objectTemplate {
  174. t := newObjectTemplate()
  175. t.protoFactory = func(r *Runtime) *Object {
  176. return r.global.ObjectPrototype
  177. }
  178. t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) })
  179. t.putStr("toExponential", func(r *Runtime) Value { return r.methodProp(r.numberproto_toExponential, "toExponential", 1) })
  180. t.putStr("toFixed", func(r *Runtime) Value { return r.methodProp(r.numberproto_toFixed, "toFixed", 1) })
  181. t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toLocaleString", 0) })
  182. t.putStr("toPrecision", func(r *Runtime) Value { return r.methodProp(r.numberproto_toPrecision, "toPrecision", 1) })
  183. t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toString", 1) })
  184. t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.numberproto_valueOf, "valueOf", 0) })
  185. return t
  186. }
  187. var numberProtoTemplate *objectTemplate
  188. var numberProtoTemplateOnce sync.Once
  189. func getNumberProtoTemplate() *objectTemplate {
  190. numberProtoTemplateOnce.Do(func() {
  191. numberProtoTemplate = createNumberProtoTemplate()
  192. })
  193. return numberProtoTemplate
  194. }
  195. func (r *Runtime) getNumberPrototype() *Object {
  196. ret := r.global.NumberPrototype
  197. if ret == nil {
  198. ret = &Object{runtime: r}
  199. r.global.NumberPrototype = ret
  200. o := r.newTemplatedObject(getNumberProtoTemplate(), ret)
  201. o.class = classNumber
  202. }
  203. return ret
  204. }
  205. func (r *Runtime) getParseFloat() *Object {
  206. ret := r.global.parseFloat
  207. if ret == nil {
  208. ret = r.newNativeFunc(r.builtin_parseFloat, "parseFloat", 1)
  209. r.global.parseFloat = ret
  210. }
  211. return ret
  212. }
  213. func (r *Runtime) getParseInt() *Object {
  214. ret := r.global.parseInt
  215. if ret == nil {
  216. ret = r.newNativeFunc(r.builtin_parseInt, "parseInt", 2)
  217. r.global.parseInt = ret
  218. }
  219. return ret
  220. }
  221. func createNumberTemplate() *objectTemplate {
  222. t := newObjectTemplate()
  223. t.protoFactory = func(r *Runtime) *Object {
  224. return r.getFunctionPrototype()
  225. }
  226. t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
  227. t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Number"), false, false, true) })
  228. t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getNumberPrototype(), false, false, false) })
  229. t.putStr("EPSILON", func(r *Runtime) Value { return valueProp(_epsilon, false, false, false) })
  230. t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.number_isFinite, "isFinite", 1) })
  231. t.putStr("isInteger", func(r *Runtime) Value { return r.methodProp(r.number_isInteger, "isInteger", 1) })
  232. t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.number_isNaN, "isNaN", 1) })
  233. t.putStr("isSafeInteger", func(r *Runtime) Value { return r.methodProp(r.number_isSafeInteger, "isSafeInteger", 1) })
  234. t.putStr("MAX_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(maxInt-1), false, false, false) })
  235. t.putStr("MIN_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(-(maxInt - 1)), false, false, false) })
  236. t.putStr("MIN_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.SmallestNonzeroFloat64), false, false, false) })
  237. t.putStr("MAX_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.MaxFloat64), false, false, false) })
  238. t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) })
  239. t.putStr("NEGATIVE_INFINITY", func(r *Runtime) Value { return valueProp(_negativeInf, false, false, false) })
  240. t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) })
  241. t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) })
  242. t.putStr("POSITIVE_INFINITY", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) })
  243. return t
  244. }
  245. var numberTemplate *objectTemplate
  246. var numberTemplateOnce sync.Once
  247. func getNumberTemplate() *objectTemplate {
  248. numberTemplateOnce.Do(func() {
  249. numberTemplate = createNumberTemplate()
  250. })
  251. return numberTemplate
  252. }
  253. func (r *Runtime) getNumber() *Object {
  254. ret := r.global.Number
  255. if ret == nil {
  256. ret = &Object{runtime: r}
  257. r.global.Number = ret
  258. r.newTemplatedFuncObject(getNumberTemplate(), ret, r.builtin_Number,
  259. r.wrapNativeConstruct(r.builtin_newNumber, ret, r.getNumberPrototype()))
  260. }
  261. return ret
  262. }