123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- package goja
- import (
- "fmt"
- "github.com/dop251/goja/ast"
- "github.com/dop251/goja/file"
- "github.com/dop251/goja/token"
- "strconv"
- )
- func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
- // log.Printf("compileStatement(): %T", v)
- switch v := v.(type) {
- case *ast.BlockStatement:
- c.compileBlockStatement(v, needResult)
- case *ast.ExpressionStatement:
- c.compileExpressionStatement(v, needResult)
- case *ast.VariableStatement:
- c.compileVariableStatement(v, needResult)
- case *ast.ReturnStatement:
- c.compileReturnStatement(v)
- case *ast.IfStatement:
- c.compileIfStatement(v, needResult)
- case *ast.DoWhileStatement:
- c.compileDoWhileStatement(v, needResult)
- case *ast.ForStatement:
- c.compileForStatement(v, needResult)
- case *ast.ForInStatement:
- c.compileForInStatement(v, needResult)
- case *ast.ForOfStatement:
- c.compileForOfStatement(v, needResult)
- case *ast.WhileStatement:
- c.compileWhileStatement(v, needResult)
- case *ast.BranchStatement:
- c.compileBranchStatement(v, needResult)
- case *ast.TryStatement:
- c.compileTryStatement(v)
- if needResult {
- c.emit(loadUndef)
- }
- case *ast.ThrowStatement:
- c.compileThrowStatement(v)
- case *ast.SwitchStatement:
- c.compileSwitchStatement(v, needResult)
- case *ast.LabelledStatement:
- c.compileLabeledStatement(v, needResult)
- case *ast.EmptyStatement:
- c.compileEmptyStatement(needResult)
- case *ast.WithStatement:
- c.compileWithStatement(v, needResult)
- case *ast.DebuggerStatement:
- default:
- panic(fmt.Errorf("Unknown statement type: %T", v))
- }
- }
- func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) {
- label := v.Label.Name
- for b := c.block; b != nil; b = b.outer {
- if b.label == label {
- c.throwSyntaxError(int(v.Label.Idx-1), "Label '%s' has already been declared", label)
- }
- }
- switch s := v.Statement.(type) {
- case *ast.ForInStatement:
- c.compileLabeledForInStatement(s, needResult, label)
- case *ast.ForOfStatement:
- c.compileLabeledForOfStatement(s, needResult, label)
- case *ast.ForStatement:
- c.compileLabeledForStatement(s, needResult, label)
- case *ast.WhileStatement:
- c.compileLabeledWhileStatement(s, needResult, label)
- case *ast.DoWhileStatement:
- c.compileLabeledDoWhileStatement(s, needResult, label)
- default:
- c.compileGenericLabeledStatement(v.Statement, needResult, label)
- }
- }
- func (c *compiler) compileTryStatement(v *ast.TryStatement) {
- if c.scope.strict && v.Catch != nil {
- switch v.Catch.Parameter.Name {
- case "arguments", "eval":
- c.throwSyntaxError(int(v.Catch.Parameter.Idx)-1, "Catch variable may not be eval or arguments in strict mode")
- }
- }
- c.block = &block{
- typ: blockTry,
- outer: c.block,
- }
- lbl := len(c.p.code)
- c.emit(nil)
- c.compileStatement(v.Body, false)
- c.emit(halt)
- lbl2 := len(c.p.code)
- c.emit(nil)
- var catchOffset int
- dynamicCatch := true
- if v.Catch != nil {
- dyn := nearestNonLexical(c.scope).dynamic
- accessed := c.scope.accessed
- c.newScope()
- c.scope.bindName(v.Catch.Parameter.Name)
- c.scope.lexical = true
- start := len(c.p.code)
- c.emit(nil)
- catchOffset = len(c.p.code) - lbl
- c.emit(enterCatch(v.Catch.Parameter.Name))
- c.compileStatement(v.Catch.Body, false)
- dyn1 := c.scope.dynamic
- accessed1 := c.scope.accessed
- c.popScope()
- if !dyn && !dyn1 && !accessed1 {
- c.scope.accessed = accessed
- dynamicCatch = false
- code := c.p.code[start+1:]
- m := make(map[uint32]uint32)
- remap := func(instr uint32) uint32 {
- level := instr >> 24
- idx := instr & 0x00FFFFFF
- if level > 0 {
- level--
- return (level << 24) | idx
- } else {
- // remap
- newIdx, exists := m[idx]
- if !exists {
- exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
- c.scope.lastFreeTmp++
- newIdx, _ = c.scope.bindName(exname)
- m[idx] = newIdx
- }
- return newIdx
- }
- }
- for pc, instr := range code {
- switch instr := instr.(type) {
- case getLocal:
- code[pc] = getLocal(remap(uint32(instr)))
- case setLocal:
- code[pc] = setLocal(remap(uint32(instr)))
- case setLocalP:
- code[pc] = setLocalP(remap(uint32(instr)))
- }
- }
- if catchVarIdx, exists := m[0]; exists {
- c.p.code[start] = setLocal(catchVarIdx)
- c.p.code[start+1] = pop
- catchOffset--
- } else {
- c.p.code[start+1] = nil
- catchOffset++
- }
- } else {
- c.scope.accessed = true
- }
- /*
- if true/*sc.dynamic/ {
- dynamicCatch = true
- c.scope.accessed = true
- c.newScope()
- c.scope.bindName(v.Catch.Parameter.Name)
- c.scope.lexical = true
- c.emit(enterCatch(v.Catch.Parameter.Name))
- c.compileStatement(v.Catch.Body, false)
- c.popScope()
- } else {
- exname := " __tmp" + strconv.Itoa(c.scope.lastFreeTmp)
- c.scope.lastFreeTmp++
- catchVarIdx, _ := c.scope.bindName(exname)
- c.emit(setLocal(catchVarIdx), pop)
- saved, wasSaved := c.scope.namesMap[v.Catch.Parameter.Name]
- c.scope.namesMap[v.Catch.Parameter.Name] = exname
- c.compileStatement(v.Catch.Body, false)
- if wasSaved {
- c.scope.namesMap[v.Catch.Parameter.Name] = saved
- } else {
- delete(c.scope.namesMap, v.Catch.Parameter.Name)
- }
- c.scope.lastFreeTmp--
- }*/
- c.emit(halt)
- }
- var finallyOffset int
- if v.Finally != nil {
- lbl1 := len(c.p.code)
- c.emit(nil)
- finallyOffset = len(c.p.code) - lbl
- c.compileStatement(v.Finally, false)
- c.emit(halt, retFinally)
- c.p.code[lbl1] = jump(len(c.p.code) - lbl1)
- }
- c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset), dynamic: dynamicCatch}
- c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
- c.leaveBlock()
- }
- func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
- //c.p.srcMap = append(c.p.srcMap, srcMapItem{pc: len(c.p.code), srcPos: int(v.Throw) - 1})
- c.compileExpression(v.Argument).emitGetter(true)
- c.emit(throw)
- }
- func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) {
- c.compileLabeledDoWhileStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label string) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- if needResult {
- c.emit(jump(2))
- }
- start := len(c.p.code)
- if needResult {
- c.emit(pop)
- }
- c.markBlockStart()
- c.compileStatement(v.Body, needResult)
- c.block.cont = len(c.p.code)
- c.emitExpr(c.compileExpression(v.Test), true)
- c.emit(jeq(start - len(c.p.code)))
- c.leaveBlock()
- }
- func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
- c.compileLabeledForStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label string) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- if v.Initializer != nil {
- c.compileExpression(v.Initializer).emitGetter(false)
- }
- if needResult {
- c.emit(loadUndef) // initial result
- }
- start := len(c.p.code)
- c.markBlockStart()
- var j int
- testConst := false
- if v.Test != nil {
- expr := c.compileExpression(v.Test)
- if expr.constant() {
- r, ex := c.evalConst(expr)
- if ex == nil {
- if r.ToBoolean() {
- testConst = true
- } else {
- // TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
- /*
- p := c.p
- c.p = &program{}
- c.compileStatement(v.Body, false)
- if v.Update != nil {
- c.compileExpression(v.Update).emitGetter(false)
- }
- c.p = p*/
- goto end
- }
- } else {
- expr.addSrcMap()
- c.emitThrow(ex.val)
- goto end
- }
- } else {
- expr.emitGetter(true)
- j = len(c.p.code)
- c.emit(nil)
- }
- }
- if needResult {
- c.emit(pop) // remove last result
- }
- c.markBlockStart()
- c.compileStatement(v.Body, needResult)
- c.block.cont = len(c.p.code)
- if v.Update != nil {
- c.compileExpression(v.Update).emitGetter(false)
- }
- c.emit(jump(start - len(c.p.code)))
- if v.Test != nil {
- if !testConst {
- c.p.code[j] = jne(len(c.p.code) - j)
- }
- }
- end:
- c.leaveBlock()
- c.markBlockStart()
- }
- func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
- c.compileLabeledForInStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label string) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- c.compileExpression(v.Source).emitGetter(true)
- c.emit(enumerate)
- if needResult {
- c.emit(loadUndef)
- }
- start := len(c.p.code)
- c.markBlockStart()
- c.block.cont = start
- c.emit(nil)
- c.compileExpression(v.Into).emitSetter(&c.enumGetExpr)
- c.emit(pop)
- if needResult {
- c.emit(pop) // remove last result
- }
- c.markBlockStart()
- c.compileStatement(v.Body, needResult)
- c.emit(jump(start - len(c.p.code)))
- c.p.code[start] = enumNext(len(c.p.code) - start)
- c.leaveBlock()
- c.markBlockStart()
- c.emit(enumPop)
- }
- func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) {
- c.compileLabeledForOfStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label string) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- c.compileExpression(v.Source).emitGetter(true)
- c.emit(iterate)
- if needResult {
- c.emit(loadUndef)
- }
- start := len(c.p.code)
- c.markBlockStart()
- c.block.cont = start
- c.emit(nil)
- c.compileExpression(v.Into).emitSetter(&c.enumGetExpr)
- c.emit(pop)
- if needResult {
- c.emit(pop) // remove last result
- }
- c.markBlockStart()
- c.compileStatement(v.Body, needResult)
- c.emit(jump(start - len(c.p.code)))
- c.p.code[start] = iterNext(len(c.p.code) - start)
- c.leaveBlock()
- c.markBlockStart()
- c.emit(enumPop)
- }
- func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
- c.compileLabeledWhileStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label string) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- if needResult {
- c.emit(loadUndef)
- }
- start := len(c.p.code)
- c.markBlockStart()
- c.block.cont = start
- expr := c.compileExpression(v.Test)
- testTrue := false
- var j int
- if expr.constant() {
- if t, ex := c.evalConst(expr); ex == nil {
- if t.ToBoolean() {
- testTrue = true
- } else {
- p := c.p
- c.p = &Program{}
- c.compileStatement(v.Body, false)
- c.p = p
- goto end
- }
- } else {
- c.emitThrow(ex.val)
- goto end
- }
- } else {
- expr.emitGetter(true)
- j = len(c.p.code)
- c.emit(nil)
- }
- if needResult {
- c.emit(pop)
- }
- c.markBlockStart()
- c.compileStatement(v.Body, needResult)
- c.emit(jump(start - len(c.p.code)))
- if !testTrue {
- c.p.code[j] = jne(len(c.p.code) - j)
- }
- end:
- c.leaveBlock()
- c.markBlockStart()
- }
- func (c *compiler) compileEmptyStatement(needResult bool) {
- if needResult {
- if len(c.p.code) == c.blockStart {
- // first statement in block, use undefined as result
- c.emit(loadUndef)
- }
- }
- }
- func (c *compiler) compileBranchStatement(v *ast.BranchStatement, needResult bool) {
- switch v.Token {
- case token.BREAK:
- c.compileBreak(v.Label, v.Idx)
- case token.CONTINUE:
- c.compileContinue(v.Label, v.Idx)
- default:
- panic(fmt.Errorf("Unknown branch statement token: %s", v.Token.String()))
- }
- }
- func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
- switch st.Token {
- case token.BREAK:
- return c.findBreakBlock(st.Label)
- case token.CONTINUE:
- return c.findContinueBlock(st.Label)
- }
- return nil
- }
- func (c *compiler) findContinueBlock(label *ast.Identifier) (block *block) {
- if label != nil {
- for b := c.block; b != nil; b = b.outer {
- if b.typ == blockLoop && b.label == label.Name {
- block = b
- break
- }
- }
- } else {
- // find the nearest loop
- for b := c.block; b != nil; b = b.outer {
- if b.typ == blockLoop {
- block = b
- break
- }
- }
- }
- return
- }
- func (c *compiler) findBreakBlock(label *ast.Identifier) (block *block) {
- if label != nil {
- for b := c.block; b != nil; b = b.outer {
- if b.label == label.Name {
- block = b
- break
- }
- }
- } else {
- // find the nearest loop or switch
- L:
- for b := c.block; b != nil; b = b.outer {
- switch b.typ {
- case blockLoop, blockSwitch:
- block = b
- break L
- }
- }
- }
- return
- }
- func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
- var block *block
- if label != nil {
- for b := c.block; b != nil; b = b.outer {
- switch b.typ {
- case blockTry:
- c.emit(halt)
- case blockWith:
- c.emit(leaveWith)
- }
- if b.label == label.Name {
- block = b
- break
- }
- }
- } else {
- // find the nearest loop or switch
- L:
- for b := c.block; b != nil; b = b.outer {
- switch b.typ {
- case blockTry:
- c.emit(halt)
- case blockWith:
- c.emit(leaveWith)
- case blockLoop, blockSwitch:
- block = b
- break L
- }
- }
- }
- if block != nil {
- if len(c.p.code) == c.blockStart && block.needResult {
- c.emit(loadUndef)
- }
- block.breaks = append(block.breaks, len(c.p.code))
- c.emit(nil)
- } else {
- c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
- }
- }
- func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
- var block *block
- if label != nil {
- for b := c.block; b != nil; b = b.outer {
- if b.typ == blockTry {
- c.emit(halt)
- } else if b.typ == blockLoop && b.label == label.Name {
- block = b
- break
- }
- }
- } else {
- // find the nearest loop
- for b := c.block; b != nil; b = b.outer {
- if b.typ == blockTry {
- c.emit(halt)
- } else if b.typ == blockLoop {
- block = b
- break
- }
- }
- }
- if block != nil {
- if len(c.p.code) == c.blockStart && block.needResult {
- c.emit(loadUndef)
- }
- block.conts = append(block.conts, len(c.p.code))
- c.emit(nil)
- } else {
- c.throwSyntaxError(int(idx)-1, "Undefined label '%s'", label.Name)
- }
- }
- func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
- test := c.compileExpression(v.Test)
- if test.constant() {
- r, ex := c.evalConst(test)
- if ex != nil {
- test.addSrcMap()
- c.emitThrow(ex.val)
- return
- }
- if r.ToBoolean() {
- c.markBlockStart()
- c.compileStatement(v.Consequent, needResult)
- if v.Alternate != nil {
- p := c.p
- c.p = &Program{}
- c.markBlockStart()
- c.compileStatement(v.Alternate, false)
- c.p = p
- }
- } else {
- // TODO: Properly implement dummy compilation (no garbage in block, scope, etc..)
- p := c.p
- c.p = &Program{}
- c.compileStatement(v.Consequent, false)
- c.p = p
- if v.Alternate != nil {
- c.compileStatement(v.Alternate, needResult)
- } else {
- if needResult {
- c.emit(loadUndef)
- }
- }
- }
- return
- }
- test.emitGetter(true)
- jmp := len(c.p.code)
- c.emit(nil)
- c.markBlockStart()
- c.compileStatement(v.Consequent, needResult)
- if v.Alternate != nil {
- jmp1 := len(c.p.code)
- c.emit(nil)
- c.p.code[jmp] = jne(len(c.p.code) - jmp)
- c.markBlockStart()
- c.compileStatement(v.Alternate, needResult)
- c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
- c.markBlockStart()
- } else {
- c.p.code[jmp] = jne(len(c.p.code) - jmp)
- c.markBlockStart()
- if needResult {
- c.emit(loadUndef)
- }
- }
- }
- func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
- if v.Argument != nil {
- c.compileExpression(v.Argument).emitGetter(true)
- //c.emit(checkResolve)
- } else {
- c.emit(loadUndef)
- }
- for b := c.block; b != nil; b = b.outer {
- if b.typ == blockTry {
- c.emit(halt)
- }
- }
- c.emit(ret)
- }
- func (c *compiler) compileVariableStatement(v *ast.VariableStatement, needResult bool) {
- for _, expr := range v.List {
- c.compileExpression(expr).emitGetter(false)
- }
- if needResult {
- c.emit(loadUndef)
- }
- }
- func (c *compiler) getFirstNonEmptyStatement(st ast.Statement) ast.Statement {
- switch st := st.(type) {
- case *ast.BlockStatement:
- return c.getFirstNonEmptyStatementList(st.List)
- case *ast.LabelledStatement:
- return c.getFirstNonEmptyStatement(st.Statement)
- }
- return st
- }
- func (c *compiler) getFirstNonEmptyStatementList(list []ast.Statement) ast.Statement {
- for _, st := range list {
- switch st := st.(type) {
- case *ast.EmptyStatement:
- continue
- case *ast.BlockStatement:
- return c.getFirstNonEmptyStatementList(st.List)
- case *ast.LabelledStatement:
- return c.getFirstNonEmptyStatement(st.Statement)
- }
- return st
- }
- return nil
- }
- func (c *compiler) compileStatements(list []ast.Statement, needResult bool) {
- if len(list) > 0 {
- cur := list[0]
- for idx := 0; idx < len(list); {
- var next ast.Statement
- // find next non-empty statement
- for idx++; idx < len(list); idx++ {
- if _, empty := list[idx].(*ast.EmptyStatement); !empty {
- next = list[idx]
- break
- }
- }
- if next != nil {
- bs := c.getFirstNonEmptyStatement(next)
- if bs, ok := bs.(*ast.BranchStatement); ok {
- block := c.findBranchBlock(bs)
- if block != nil {
- c.compileStatement(cur, block.needResult)
- cur = next
- continue
- }
- }
- c.compileStatement(cur, false)
- cur = next
- } else {
- c.compileStatement(cur, needResult)
- }
- }
- } else {
- if needResult {
- c.emit(loadUndef)
- }
- }
- }
- func (c *compiler) compileGenericLabeledStatement(v ast.Statement, needResult bool, label string) {
- c.block = &block{
- typ: blockBranch,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- c.compileStatement(v, needResult)
- c.leaveBlock()
- }
- func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) {
- c.compileStatements(v.List, needResult)
- }
- func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) {
- expr := c.compileExpression(v.Expression)
- if expr.constant() {
- c.emitConst(expr, needResult)
- } else {
- expr.emitGetter(needResult)
- }
- }
- func (c *compiler) compileWithStatement(v *ast.WithStatement, needResult bool) {
- if c.scope.strict {
- c.throwSyntaxError(int(v.With)-1, "Strict mode code may not include a with statement")
- return
- }
- c.compileExpression(v.Object).emitGetter(true)
- c.emit(enterWith)
- c.block = &block{
- outer: c.block,
- typ: blockWith,
- needResult: needResult,
- }
- c.newScope()
- c.scope.dynamic = true
- c.scope.lexical = true
- c.compileStatement(v.Body, needResult)
- c.emit(leaveWith)
- c.leaveBlock()
- c.popScope()
- }
- func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult bool) {
- c.block = &block{
- typ: blockSwitch,
- outer: c.block,
- needResult: needResult,
- }
- c.compileExpression(v.Discriminant).emitGetter(true)
- jumps := make([]int, len(v.Body))
- for i, s := range v.Body {
- if s.Test != nil {
- c.emit(dup)
- c.compileExpression(s.Test).emitGetter(true)
- c.emit(op_strict_eq)
- c.emit(jne(3), pop)
- jumps[i] = len(c.p.code)
- c.emit(nil)
- }
- }
- c.emit(pop)
- jumpNoMatch := -1
- if v.Default != -1 {
- if v.Default != 0 {
- jumps[v.Default] = len(c.p.code)
- c.emit(nil)
- }
- } else {
- jumpNoMatch = len(c.p.code)
- c.emit(nil)
- }
- for i, s := range v.Body {
- if s.Test != nil || i != 0 {
- c.p.code[jumps[i]] = jump(len(c.p.code) - jumps[i])
- c.markBlockStart()
- }
- nr := false
- c.markBlockStart()
- if needResult {
- if i < len(v.Body)-1 {
- st := c.getFirstNonEmptyStatementList(v.Body[i+1].Consequent)
- if st, ok := st.(*ast.BranchStatement); ok && st.Token == token.BREAK {
- if c.findBreakBlock(st.Label) != nil {
- stmts := append(s.Consequent, st)
- c.compileStatements(stmts, false)
- continue
- }
- }
- } else {
- nr = true
- }
- }
- c.compileStatements(s.Consequent, nr)
- }
- if jumpNoMatch != -1 {
- if needResult {
- c.emit(jump(2))
- }
- c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch)
- if needResult {
- c.emit(loadUndef)
- }
- }
- c.leaveBlock()
- c.markBlockStart()
- }
|