compiler_stmt.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. package goja
  2. import (
  3. "fmt"
  4. "github.com/dop251/goja/ast"
  5. "github.com/dop251/goja/file"
  6. "github.com/dop251/goja/token"
  7. "strconv"
  8. )
  9. func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
  10. // log.Printf("compileStatement(): %T", v)
  11. switch v := v.(type) {
  12. case *ast.BlockStatement:
  13. c.compileBlockStatement(v, needResult)
  14. case *ast.ExpressionStatement:
  15. c.compileExpressionStatement(v, needResult)
  16. case *ast.VariableStatement:
  17. c.compileVariableStatement(v, needResult)
  18. case *ast.ReturnStatement:
  19. c.compileReturnStatement(v)
  20. case *ast.IfStatement:
  21. c.compileIfStatement(v, needResult)
  22. case *ast.DoWhileStatement:
  23. c.compileDoWhileStatement(v, needResult)
  24. case *ast.ForStatement:
  25. c.compileForStatement(v, needResult)
  26. case *ast.ForInStatement:
  27. c.compileForInStatement(v, needResult)
  28. case *ast.WhileStatement:
  29. c.compileWhileStatement(v, needResult)
  30. case *ast.BranchStatement:
  31. c.compileBranchStatement(v, needResult)
  32. case *ast.TryStatement:
  33. c.compileTryStatement(v)
  34. if needResult {
  35. c.emit(loadUndef)
  36. }
  37. case *ast.ThrowStatement:
  38. c.compileThrowStatement(v)
  39. case *ast.SwitchStatement:
  40. c.compileSwitchStatement(v, needResult)
  41. case *ast.LabelledStatement:
  42. c.compileLabeledStatement(v, needResult)
  43. case *ast.EmptyStatement:
  44. if needResult {
  45. c.emit(loadUndef)
  46. }
  47. case *ast.WithStatement:
  48. c.compileWithStatement(v, needResult)
  49. default:
  50. panic(fmt.Errorf("Unknown statement type: %T", v))
  51. }
  52. }
  53. func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) {
  54. label := v.Label.Name
  55. for b := c.block; b != nil; b = b.outer {
  56. if b.label == label {
  57. c.throwSyntaxError(int(v.Label.Idx-1), "Label '%s' has already been declared", label)
  58. }
  59. }
  60. switch s := v.Statement.(type) {
  61. case *ast.BlockStatement:
  62. c.compileLabeledBlockStatement(s, needResult, label)
  63. case *ast.ForInStatement:
  64. c.compileLabeledForInStatement(s, needResult, label)
  65. case *ast.ForStatement:
  66. c.compileLabeledForStatement(s, needResult, label)
  67. case *ast.WhileStatement:
  68. c.compileLabeledWhileStatement(s, needResult, label)
  69. case *ast.DoWhileStatement:
  70. c.compileLabeledDoWhileStatement(s, needResult, label)
  71. default:
  72. c.compileStatement(v.Statement, needResult)
  73. }
  74. }
  75. func (c *compiler) compileTryStatement(v *ast.TryStatement) {
  76. if c.scope.strict && v.Catch != nil {
  77. switch v.Catch.Parameter.Name {
  78. case "arguments", "eval":
  79. c.throwSyntaxError(int(v.Catch.Parameter.Idx)-1, "Catch variable may not be eval or arguments in strict mode")
  80. }
  81. }
  82. c.block = &block{
  83. typ: blockTry,
  84. outer: c.block,
  85. }
  86. lbl := len(c.p.code)
  87. c.emit(nil)
  88. c.compileStatement(v.Body, false)
  89. c.emit(halt)
  90. lbl2 := len(c.p.code)
  91. c.emit(nil)
  92. var catchOffset int
  93. dynamicCatch := true
  94. if v.Catch != nil {
  95. dyn := nearestNonLexical(c.scope).dynamic
  96. accessed := c.scope.accessed
  97. c.newScope()
  98. c.scope.bindName(v.Catch.Parameter.Name)
  99. c.scope.lexical = true
  100. start := len(c.p.code)
  101. c.emit(nil)
  102. catchOffset = len(c.p.code) - lbl
  103. c.emit(enterCatch(v.Catch.Parameter.Name))
  104. c.compileStatement(v.Catch.Body, false)
  105. dyn1 := c.scope.dynamic
  106. accessed1 := c.scope.accessed
  107. c.popScope()
  108. if !dyn && !dyn1 && !accessed1 {
  109. c.scope.accessed = accessed
  110. dynamicCatch = false
  111. code := c.p.code[start+1:]
  112. m := make(map[uint32]uint32)
  113. remap := func(instr uint32) uint32 {
  114. level := instr >> 24
  115. idx := instr & 0x00FFFFFF
  116. if level > 0 {
  117. level--
  118. return (level << 24) | idx
  119. } else {
  120. // remap
  121. newIdx, exists := m[idx]
  122. if !exists {
  123. exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
  124. c.scope.lastFreeTmp++
  125. newIdx, _ = c.scope.bindName(exname)
  126. m[idx] = newIdx
  127. }
  128. return newIdx
  129. }
  130. }
  131. for pc, instr := range code {
  132. switch instr := instr.(type) {
  133. case getLocal:
  134. code[pc] = getLocal(remap(uint32(instr)))
  135. case setLocal:
  136. code[pc] = setLocal(remap(uint32(instr)))
  137. case setLocalP:
  138. code[pc] = setLocalP(remap(uint32(instr)))
  139. }
  140. }
  141. if catchVarIdx, exists := m[0]; exists {
  142. c.p.code[start] = setLocal(catchVarIdx)
  143. c.p.code[start+1] = pop
  144. catchOffset--
  145. } else {
  146. c.p.code[start+1] = nil
  147. catchOffset++
  148. }
  149. } else {
  150. c.scope.accessed = true
  151. }
  152. /*
  153. if true/*sc.dynamic/ {
  154. dynamicCatch = true
  155. c.scope.accessed = true
  156. c.newScope()
  157. c.scope.bindName(v.Catch.Parameter.Name)
  158. c.scope.lexical = true
  159. c.emit(enterCatch(v.Catch.Parameter.Name))
  160. c.compileStatement(v.Catch.Body, false)
  161. c.popScope()
  162. } else {
  163. exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
  164. c.scope.lastFreeTmp++
  165. catchVarIdx, _ := c.scope.bindName(exname)
  166. c.emit(setLocal(catchVarIdx), pop)
  167. saved, wasSaved := c.scope.namesMap[v.Catch.Parameter.Name]
  168. c.scope.namesMap[v.Catch.Parameter.Name] = exname
  169. c.compileStatement(v.Catch.Body, false)
  170. if wasSaved {
  171. c.scope.namesMap[v.Catch.Parameter.Name] = saved
  172. } else {
  173. delete(c.scope.namesMap, v.Catch.Parameter.Name)
  174. }
  175. c.scope.lastFreeTmp--
  176. }*/
  177. c.emit(halt)
  178. }
  179. var finallyOffset int
  180. if v.Finally != nil {
  181. lbl1 := len(c.p.code)
  182. c.emit(nil)
  183. finallyOffset = len(c.p.code) - lbl
  184. c.compileStatement(v.Finally, false)
  185. c.emit(halt, retFinally)
  186. c.p.code[lbl1] = jump(len(c.p.code) - lbl1)
  187. }
  188. c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset), dynamic: dynamicCatch}
  189. c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
  190. c.leaveBlock()
  191. }
  192. func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
  193. //c.p.srcMap = append(c.p.srcMap, srcMapItem{pc: len(c.p.code), srcPos: int(v.Throw) - 1})
  194. c.compileExpression(v.Argument).emitGetter(true)
  195. c.emit(throw)
  196. }
  197. func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) {
  198. c.compileLabeledDoWhileStatement(v, needResult, "")
  199. }
  200. func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label string) {
  201. c.block = &block{
  202. typ: blockLoop,
  203. outer: c.block,
  204. label: label,
  205. }
  206. if needResult {
  207. c.emit(loadUndef)
  208. }
  209. start := len(c.p.code)
  210. c.markBlockStart()
  211. c.compileStatement(v.Body, needResult)
  212. if needResult {
  213. c.emit(rdupN(1), pop)
  214. }
  215. c.block.cont = len(c.p.code)
  216. c.emitExpr(c.compileExpression(v.Test), true)
  217. c.emit(jeq(start - len(c.p.code)))
  218. c.leaveBlock()
  219. }
  220. func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
  221. c.compileLabeledForStatement(v, needResult, "")
  222. }
  223. func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label string) {
  224. c.block = &block{
  225. typ: blockLoop,
  226. outer: c.block,
  227. label: label,
  228. needResult: needResult,
  229. }
  230. if v.Initializer != nil {
  231. c.compileExpression(v.Initializer).emitGetter(false)
  232. }
  233. if needResult {
  234. c.emit(loadUndef)
  235. }
  236. start := len(c.p.code)
  237. c.markBlockStart()
  238. var j int
  239. testConst := false
  240. if v.Test != nil {
  241. expr := c.compileExpression(v.Test)
  242. if expr.constant() {
  243. r, ex := c.evalConst(expr)
  244. if ex == nil {
  245. if r.ToBoolean() {
  246. testConst = true
  247. } else {
  248. // TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
  249. /*
  250. p := c.p
  251. c.p = &program{}
  252. c.compileStatement(v.Body, false)
  253. if v.Update != nil {
  254. c.compileExpression(v.Update).emitGetter(false)
  255. }
  256. c.p = p*/
  257. goto end
  258. }
  259. } else {
  260. expr.addSrcMap()
  261. c.emitThrow(ex.val)
  262. goto end
  263. }
  264. } else {
  265. expr.emitGetter(true)
  266. j = len(c.p.code)
  267. c.emit(nil)
  268. }
  269. }
  270. c.compileStatement(v.Body, needResult)
  271. if needResult {
  272. c.emit(rdupN(1), pop)
  273. }
  274. c.block.cont = len(c.p.code)
  275. if v.Update != nil {
  276. c.compileExpression(v.Update).emitGetter(false)
  277. }
  278. c.emit(jump(start - len(c.p.code)))
  279. if v.Test != nil {
  280. if !testConst {
  281. c.p.code[j] = jne(len(c.p.code) - j)
  282. }
  283. }
  284. end:
  285. c.leaveBlock()
  286. c.markBlockStart()
  287. }
  288. func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
  289. c.compileLabeledForInStatement(v, needResult, "")
  290. }
  291. func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label string) {
  292. c.block = &block{
  293. typ: blockLoop,
  294. outer: c.block,
  295. label: label,
  296. needResult: needResult,
  297. }
  298. c.compileExpression(v.Source).emitGetter(true)
  299. c.emit(enumerate)
  300. if needResult {
  301. c.emit(loadUndef)
  302. }
  303. start := len(c.p.code)
  304. c.markBlockStart()
  305. c.block.cont = start
  306. c.emit(nil)
  307. c.compileExpression(v.Into).emitSetter(&c.enumGetExpr)
  308. c.emit(pop)
  309. c.compileStatement(v.Body, needResult)
  310. if needResult {
  311. c.emit(rdupN(1), pop)
  312. }
  313. c.emit(jump(start - len(c.p.code)))
  314. c.p.code[start] = enumNext(len(c.p.code) - start)
  315. c.leaveBlock()
  316. c.markBlockStart()
  317. c.emit(enumPop)
  318. }
  319. func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
  320. c.compileLabeledWhileStatement(v, needResult, "")
  321. }
  322. func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label string) {
  323. c.block = &block{
  324. typ: blockLoop,
  325. outer: c.block,
  326. label: label,
  327. }
  328. if needResult {
  329. c.emit(loadUndef)
  330. }
  331. start := len(c.p.code)
  332. c.markBlockStart()
  333. c.block.cont = start
  334. expr := c.compileExpression(v.Test)
  335. testTrue := false
  336. var j int
  337. if expr.constant() {
  338. if t, ex := c.evalConst(expr); ex == nil {
  339. if t.ToBoolean() {
  340. testTrue = true
  341. } else {
  342. p := c.p
  343. c.p = &Program{}
  344. c.compileStatement(v.Body, false)
  345. c.p = p
  346. goto end
  347. }
  348. } else {
  349. c.emitThrow(ex.val)
  350. goto end
  351. }
  352. } else {
  353. expr.emitGetter(true)
  354. j = len(c.p.code)
  355. c.emit(nil)
  356. }
  357. c.compileStatement(v.Body, needResult)
  358. if needResult {
  359. c.emit(rdupN(1), pop)
  360. }
  361. c.emit(jump(start - len(c.p.code)))
  362. if !testTrue {
  363. c.p.code[j] = jne(len(c.p.code) - j)
  364. }
  365. end:
  366. c.leaveBlock()
  367. c.markBlockStart()
  368. }
  369. func (c *compiler) compileBranchStatement(v *ast.BranchStatement, needResult bool) {
  370. if needResult {
  371. if c.p.code[len(c.p.code)-1] != pop {
  372. // panic("Not pop")
  373. } else {
  374. c.p.code = c.p.code[:len(c.p.code)-1]
  375. }
  376. }
  377. switch v.Token {
  378. case token.BREAK:
  379. c.compileBreak(v.Label, v.Idx)
  380. case token.CONTINUE:
  381. c.compileContinue(v.Label, v.Idx)
  382. default:
  383. panic(fmt.Errorf("Unknown branch statement token: %s", v.Token.String()))
  384. }
  385. }
  386. func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
  387. var block *block
  388. if label != nil {
  389. for b := c.block; b != nil; b = b.outer {
  390. switch b.typ {
  391. case blockTry:
  392. c.emit(halt)
  393. case blockWith:
  394. c.emit(leaveWith)
  395. }
  396. if b.label == label.Name {
  397. block = b
  398. break
  399. }
  400. }
  401. } else {
  402. // find the nearest loop or switch
  403. L:
  404. for b := c.block; b != nil; b = b.outer {
  405. switch b.typ {
  406. case blockTry:
  407. c.emit(halt)
  408. case blockWith:
  409. c.emit(leaveWith)
  410. case blockLoop, blockSwitch:
  411. block = b
  412. break L
  413. }
  414. }
  415. }
  416. if block != nil {
  417. if block.needResult {
  418. c.emit(pop, loadUndef)
  419. }
  420. block.breaks = append(block.breaks, len(c.p.code))
  421. c.emit(nil)
  422. } else {
  423. c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
  424. }
  425. }
  426. func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
  427. var block *block
  428. if label != nil {
  429. for b := c.block; b != nil; b = b.outer {
  430. if b.typ == blockTry {
  431. c.emit(halt)
  432. } else if b.typ == blockLoop && b.label == label.Name {
  433. block = b
  434. break
  435. }
  436. }
  437. } else {
  438. // find the nearest loop
  439. for b := c.block; b != nil; b = b.outer {
  440. if b.typ == blockTry {
  441. c.emit(halt)
  442. } else if b.typ == blockLoop {
  443. block = b
  444. break
  445. }
  446. }
  447. }
  448. if block != nil {
  449. block.conts = append(block.conts, len(c.p.code))
  450. c.emit(nil)
  451. } else {
  452. c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
  453. }
  454. }
  455. func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
  456. test := c.compileExpression(v.Test)
  457. if test.constant() {
  458. r, ex := c.evalConst(test)
  459. if ex != nil {
  460. test.addSrcMap()
  461. c.emitThrow(ex.val)
  462. return
  463. }
  464. if r.ToBoolean() {
  465. c.compileStatement(v.Consequent, needResult)
  466. if v.Alternate != nil {
  467. p := c.p
  468. c.p = &Program{}
  469. c.compileStatement(v.Alternate, false)
  470. c.p = p
  471. }
  472. } else {
  473. // TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
  474. p := c.p
  475. c.p = &Program{}
  476. c.compileStatement(v.Consequent, false)
  477. c.p = p
  478. if v.Alternate != nil {
  479. c.compileStatement(v.Alternate, needResult)
  480. } else {
  481. if needResult {
  482. c.emit(loadUndef)
  483. }
  484. }
  485. }
  486. return
  487. }
  488. test.emitGetter(true)
  489. jmp := len(c.p.code)
  490. c.emit(nil)
  491. c.compileStatement(v.Consequent, needResult)
  492. if v.Alternate != nil {
  493. jmp1 := len(c.p.code)
  494. c.emit(nil)
  495. c.p.code[jmp] = jne(len(c.p.code) - jmp)
  496. c.markBlockStart()
  497. c.compileStatement(v.Alternate, needResult)
  498. c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
  499. c.markBlockStart()
  500. } else {
  501. c.p.code[jmp] = jne(len(c.p.code) - jmp)
  502. c.markBlockStart()
  503. if needResult {
  504. c.emit(loadUndef)
  505. }
  506. }
  507. }
  508. func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
  509. if v.Argument != nil {
  510. c.compileExpression(v.Argument).emitGetter(true)
  511. //c.emit(checkResolve)
  512. } else {
  513. c.emit(loadUndef)
  514. }
  515. for b := c.block; b != nil; b = b.outer {
  516. if b.typ == blockTry {
  517. c.emit(halt)
  518. }
  519. }
  520. c.emit(ret)
  521. }
  522. func (c *compiler) compileVariableStatement(v *ast.VariableStatement, needResult bool) {
  523. for _, expr := range v.List {
  524. c.compileExpression(expr).emitGetter(false)
  525. }
  526. if needResult {
  527. c.emit(loadUndef)
  528. }
  529. }
  530. func (c *compiler) compileStatements(list []ast.Statement, needResult bool) {
  531. if len(list) > 0 {
  532. for _, s := range list[:len(list)-1] {
  533. c.compileStatement(s, needResult)
  534. if needResult {
  535. c.emit(pop)
  536. }
  537. }
  538. c.compileStatement(list[len(list)-1], needResult)
  539. } else {
  540. if needResult {
  541. c.emit(loadUndef)
  542. }
  543. }
  544. }
  545. func (c *compiler) compileLabeledBlockStatement(v *ast.BlockStatement, needResult bool, label string) {
  546. c.block = &block{
  547. typ: blockBranch,
  548. outer: c.block,
  549. label: label,
  550. }
  551. c.compileBlockStatement(v, needResult)
  552. c.leaveBlock()
  553. }
  554. func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) {
  555. c.compileStatements(v.List, needResult)
  556. }
  557. func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) {
  558. expr := c.compileExpression(v.Expression)
  559. if expr.constant() {
  560. c.emitConst(expr, needResult)
  561. } else {
  562. expr.emitGetter(needResult)
  563. }
  564. }
  565. func (c *compiler) compileWithStatement(v *ast.WithStatement, needResult bool) {
  566. if c.scope.strict {
  567. c.throwSyntaxError(int(v.With)-1, "Strict mode code may not include a with statement")
  568. return
  569. }
  570. c.compileExpression(v.Object).emitGetter(true)
  571. c.emit(enterWith)
  572. c.block = &block{
  573. outer: c.block,
  574. typ: blockWith,
  575. }
  576. c.newScope()
  577. c.scope.dynamic = true
  578. c.scope.lexical = true
  579. c.compileStatement(v.Body, needResult)
  580. c.emit(leaveWith)
  581. c.leaveBlock()
  582. c.popScope()
  583. }
  584. func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult bool) {
  585. c.block = &block{
  586. typ: blockSwitch,
  587. outer: c.block,
  588. }
  589. if needResult {
  590. c.emit(loadUndef)
  591. }
  592. c.compileExpression(v.Discriminant).emitGetter(true)
  593. jumps := make([]int, len(v.Body))
  594. for i, s := range v.Body {
  595. if s.Test != nil {
  596. c.emit(dup)
  597. c.compileExpression(s.Test).emitGetter(true)
  598. c.emit(op_strict_eq)
  599. c.emit(jne(3), pop)
  600. jumps[i] = len(c.p.code)
  601. c.emit(nil)
  602. }
  603. }
  604. c.emit(pop)
  605. jumpNoMatch := -1
  606. if v.Default != -1 {
  607. if v.Default != 0 {
  608. jumps[v.Default] = len(c.p.code)
  609. c.emit(nil)
  610. }
  611. } else {
  612. jumpNoMatch = len(c.p.code)
  613. c.emit(nil)
  614. }
  615. for i, s := range v.Body {
  616. if s.Test != nil || i != 0 {
  617. c.p.code[jumps[i]] = jump(len(c.p.code) - jumps[i])
  618. c.markBlockStart()
  619. }
  620. if needResult {
  621. c.emit(pop)
  622. }
  623. c.compileStatements(s.Consequent, needResult)
  624. }
  625. if jumpNoMatch != -1 {
  626. c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch)
  627. }
  628. c.leaveBlock()
  629. c.markBlockStart()
  630. }