compiler_stmt.go 16 KB

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