/tests/expression.test.ts

https://github.com/Siubaak/sval · TypeScript · 370 lines · 314 code · 44 blank · 12 comment · 12 complexity · 25d9a1d41f223d6b22e5abf74ae9be82 MD5 · raw file

  1. import Sval from '../src'
  2. describe('testing src/expression.ts', () => {
  3. it('should call expression run normally', () => {
  4. const interpreter = new Sval()
  5. class A {
  6. a = 1
  7. then() {
  8. this.a++
  9. return this
  10. }
  11. }
  12. interpreter.import({ A })
  13. interpreter.run('exports.inst = new A().then()')
  14. expect(interpreter.exports.inst.a).toBe(2)
  15. })
  16. it('should unary expression run normally', () => {
  17. const interpreter = new Sval()
  18. const code = `
  19. exports.a = !(~(+(-1)))
  20. exports.b = void 0
  21. exports.c = typeof x // shouldn't throw err
  22. exports.d = 1
  23. exports.e = delete exports.d
  24. exports.f = typeof exports.e
  25. `
  26. interpreter.run(`!async function(){${code}}()`) // also test for generator env
  27. interpreter.run(code)
  28. expect(interpreter.exports.a).toBeTruthy()
  29. expect(interpreter.exports.b).toBeUndefined()
  30. expect(interpreter.exports.c).toBe('undefined')
  31. expect(interpreter.exports.d).toBeUndefined()
  32. expect(interpreter.exports.e).toBeTruthy()
  33. expect(interpreter.exports.f).toBe('boolean')
  34. })
  35. it('should binary expression run normally', () => {
  36. const interpreter = new Sval()
  37. const code = `
  38. // comparison
  39. exports.a = 1 == '1'
  40. exports.b = 1 != '1'
  41. exports.c = 1 === '1'
  42. exports.d = 1 !== '1'
  43. exports.e = 1 < 1
  44. exports.f = 1 <= 1
  45. exports.g = 1 > 1
  46. exports.h = 1 >= 1
  47. // bitwise offset
  48. exports.i = 1 << 1
  49. exports.j = 1 >> 1
  50. exports.k = 1 >>> 1
  51. exports.l = 1 | 2 // 01 | 10 = 11
  52. exports.m = 1 & 2 // 01 & 10 = 00
  53. exports.n = 1 ^ 1 // 01 ^ 01 = 00
  54. // calculate
  55. exports.o = 1 + 1
  56. exports.p = 1 - 1
  57. exports.q = 1 * 2
  58. exports.r = 2 / 2
  59. exports.s = 2 ** 2
  60. exports.t = 3 % 2
  61. // others
  62. const a = { b: 1 }
  63. exports.u = 'b' in a
  64. function b() {}
  65. const c = new b
  66. exports.v = c instanceof b
  67. `
  68. interpreter.run(`!async function(){${code}}()`) // also test for generator env
  69. interpreter.run(code)
  70. // comparison
  71. expect(interpreter.exports.a).toBeTruthy()
  72. expect(interpreter.exports.b).toBeFalsy()
  73. expect(interpreter.exports.c).toBeFalsy()
  74. expect(interpreter.exports.d).toBeTruthy()
  75. expect(interpreter.exports.e).toBeFalsy()
  76. expect(interpreter.exports.f).toBeTruthy()
  77. expect(interpreter.exports.g).toBeFalsy()
  78. expect(interpreter.exports.h).toBeTruthy()
  79. // bitwise offset
  80. expect(interpreter.exports.i).toBe(2)
  81. expect(interpreter.exports.j).toBe(0)
  82. expect(interpreter.exports.k).toBe(0)
  83. expect(interpreter.exports.l).toBe(3)
  84. expect(interpreter.exports.m).toBe(0)
  85. expect(interpreter.exports.n).toBe(0)
  86. // calculate
  87. expect(interpreter.exports.o).toBe(2)
  88. expect(interpreter.exports.p).toBe(0)
  89. expect(interpreter.exports.q).toBe(2)
  90. expect(interpreter.exports.r).toBe(1)
  91. expect(interpreter.exports.s).toBe(4)
  92. expect(interpreter.exports.t).toBe(1)
  93. // others
  94. expect(interpreter.exports.u).toBeTruthy()
  95. expect(interpreter.exports.v).toBeTruthy()
  96. })
  97. it('should assignment expression run normally', () => {
  98. const interpreter = new Sval()
  99. const code = `
  100. exports.a = 2
  101. expect(exports.a).toBe(2)
  102. exports.a -= 1
  103. expect(exports.a).toBe(1)
  104. exports.a *= 2
  105. expect(exports.a).toBe(2)
  106. exports.a **= 2
  107. expect(exports.a).toBe(4)
  108. exports.a /= 2
  109. expect(exports.a).toBe(2)
  110. exports.a %= 1
  111. expect(exports.a).toBe(0)
  112. exports.a += 1
  113. expect(exports.a).toBe(1)
  114. exports.a <<= 2
  115. expect(exports.a).toBe(4)
  116. exports.a >>= 1
  117. expect(exports.a).toBe(2)
  118. exports.a >>>= 1
  119. expect(exports.a).toBe(1)
  120. exports.a |= 1
  121. expect(exports.a).toBe(1)
  122. exports.a &= 1
  123. expect(exports.a).toBe(1)
  124. exports.a ^= 1
  125. expect(exports.a).toBe(0)
  126. `
  127. interpreter.import({ expect })
  128. interpreter.run(`!async function(){${code}}()`) // also test for generator env
  129. interpreter.run(code)
  130. })
  131. it ('should throw TypeError when assigning to constant', () => {
  132. const interpreter = new Sval()
  133. let error = null
  134. try {
  135. interpreter.run(`
  136. const x = 5
  137. x = 6
  138. `)
  139. } catch (ex) {
  140. error = ex
  141. }
  142. expect(error).toBeInstanceOf(TypeError)
  143. })
  144. it('should parse spread element normally', () => {
  145. const interpreter = new Sval()
  146. interpreter.run(`
  147. const arr = [1, 2]
  148. exports.a = [...arr]
  149. exports.b = [...[1, 2, 3]]
  150. f(...arr)
  151. function f(m, n) {
  152. exports.c = m
  153. exports.d = n
  154. }
  155. `)
  156. expect(interpreter.exports.a).toEqual([1, 2])
  157. expect(interpreter.exports.b).toEqual([1, 2, 3])
  158. expect(interpreter.exports.c).toBe(1)
  159. expect(interpreter.exports.d).toBe(2)
  160. })
  161. it('should parse regular expression normally', () => {
  162. const interpreter = new Sval()
  163. interpreter.import({ expect })
  164. interpreter.run(`
  165. const re = /\\/\\*<([^>]+?)>\\*\\/([\\s\\S]*?)\\/\\*<\\/([^>]+?)>\\*\\//g
  166. exports.a = '/*<add>*//*hello*//*</add>*/ /*<add>*//*world*//*</add>*/'
  167. .replace(re, (_, start, content, end) => {
  168. expect(start).toBe('add')
  169. expect(end).toBe('add')
  170. return content.match(/\\/\\*([\\s\\S]*)\\*\\//)[1]
  171. })
  172. `)
  173. expect(interpreter.exports.a).toBe('hello world')
  174. })
  175. it('should support object expression', () => {
  176. const interpreter = new Sval()
  177. interpreter.import({ expect })
  178. interpreter.run(`
  179. const name = 'y'
  180. const values = { a: 1, b: 2 }
  181. const a = {
  182. x: 5,
  183. [name]: 6,
  184. ...values
  185. }
  186. expect(a).toEqual(result = {
  187. x: 5,
  188. y: 6,
  189. a: 1,
  190. b: 2
  191. })
  192. // object with getter+setter
  193. const b = {
  194. _t: 1,
  195. get t() {
  196. return this._t
  197. },
  198. set t(v) {
  199. this._t = v
  200. }
  201. }
  202. b.t = 2
  203. exports.b = b
  204. `)
  205. const b = {
  206. _t: 1,
  207. get t() {
  208. return this._t
  209. },
  210. set t(v) {
  211. this._t = v
  212. }
  213. }
  214. b.t = 2
  215. expect(interpreter.exports.b).toEqual(b)
  216. })
  217. it('should support object expression with correct property descriptor', () => {
  218. const interpreter = new Sval()
  219. interpreter.run(`
  220. const a = {
  221. x: 5,
  222. get y() {
  223. return this.x + 1
  224. },
  225. set y(v) {
  226. this.x = v - 1
  227. }
  228. }
  229. exports.a = a
  230. `)
  231. const a = interpreter.exports.a;
  232. expect(Object.keys(a)).toEqual(['x', 'y'])
  233. const xPD = Object.getOwnPropertyDescriptor(a, 'x')
  234. expect(xPD).toEqual({
  235. configurable: true,
  236. enumerable: true,
  237. value: 5,
  238. writable: true
  239. })
  240. const yPD = Object.getOwnPropertyDescriptor(a, 'y')
  241. expect({
  242. configurable: yPD.configurable,
  243. enumerable: yPD.enumerable,
  244. }).toEqual({
  245. configurable: true,
  246. enumerable: true,
  247. })
  248. })
  249. it('should support logic expression', () => {
  250. const interpreter = new Sval()
  251. interpreter.import({ expect })
  252. interpreter.run(`
  253. const x = 0
  254. const y = true
  255. expect(x && y).toBe(0)
  256. expect(x || y).toBe(true)
  257. `)
  258. })
  259. it('should support method call with super + getter', () => {
  260. const interpreter = new Sval()
  261. interpreter.run(`
  262. class X {
  263. get say() {
  264. return function() { return 1}
  265. }
  266. }
  267. class Y extends X {
  268. say() {
  269. return super.say()
  270. }
  271. }
  272. exports.result = new Y().say()
  273. `)
  274. expect(interpreter.exports.result).toEqual(1);
  275. })
  276. it('should support method call with computed name', () => {
  277. const interpreter = new Sval()
  278. interpreter.run(`
  279. var x = {
  280. say() {
  281. return 1
  282. }
  283. }
  284. exports.result = x['say']()
  285. `)
  286. expect(interpreter.exports.result).toEqual(1);
  287. })
  288. it('should support method call with computed name', () => {
  289. const interpreter = new Sval()
  290. interpreter.run(`
  291. exports.result = 1+!!2
  292. `)
  293. expect(interpreter.exports.result).toEqual(2);
  294. })
  295. it('should support all kinds of delete actions', () => {
  296. const interpreter = new Sval()
  297. interpreter.run(`
  298. var x = {}
  299. // normal behavior for 'delete' in strict mode
  300. delete x.x
  301. // delete any literal except undefined, undefined is an identifier in js
  302. let result = true
  303. result &= delete 1
  304. result &= '1'
  305. result &= delete true
  306. result &= delete Symbol('xx')
  307. result &= delete null
  308. result &= delete {}
  309. result &= delete function () {}
  310. result &= delete /x/
  311. exports.result = result
  312. `)
  313. expect(interpreter.exports.result).toBeTruthy()
  314. let error = null;
  315. try {
  316. interpreter.run(`
  317. // trying to delete a regular identifier in strict mode
  318. var y = {}
  319. delete y
  320. `)
  321. } catch (ex) {
  322. error = ex
  323. }
  324. expect(error).toBeInstanceOf(SyntaxError);
  325. })
  326. })