12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 |
- package goja
- import (
- "github.com/dop251/goja/ast"
- "github.com/dop251/goja/file"
- "github.com/dop251/goja/token"
- "github.com/dop251/goja/unistring"
- )
- func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
- switch v := v.(type) {
- case *ast.BlockStatement:
- c.compileBlockStatement(v, needResult)
- case *ast.ExpressionStatement:
- c.compileExpressionStatement(v, needResult)
- case *ast.VariableStatement:
- c.compileVariableStatement(v)
- case *ast.LexicalDeclaration:
- c.compileLexicalDeclaration(v)
- 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)
- case *ast.TryStatement:
- c.compileTryStatement(v, needResult)
- 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.FunctionDeclaration:
- c.compileStandaloneFunctionDecl(v)
- // note functions inside blocks are hoisted to the top of the block and are compiled using compileFunctions()
- case *ast.ClassDeclaration:
- c.compileClassDeclaration(v)
- case *ast.WithStatement:
- c.compileWithStatement(v, needResult)
- case *ast.DebuggerStatement:
- default:
- c.assert(false, int(v.Idx0())-1, "Unknown statement type: %T", v)
- panic("unreachable")
- }
- }
- func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) {
- label := v.Label.Name
- if c.scope.strict {
- c.checkIdentifierName(label, int(v.Label.Idx)-1)
- }
- 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(s, needResult, label)
- }
- }
- func (c *compiler) updateEnterBlock(enter *enterBlock) {
- scope := c.scope
- stashSize, stackSize := 0, 0
- if scope.dynLookup {
- stashSize = len(scope.bindings)
- enter.names = scope.makeNamesMap()
- } else {
- for _, b := range scope.bindings {
- if b.inStash {
- stashSize++
- } else {
- stackSize++
- }
- }
- }
- enter.stashSize, enter.stackSize = uint32(stashSize), uint32(stackSize)
- }
- func (c *compiler) compileTryStatement(v *ast.TryStatement, needResult bool) {
- c.block = &block{
- typ: blockTry,
- outer: c.block,
- }
- var lp int
- var bodyNeedResult bool
- var finallyBreaking *block
- if v.Finally != nil {
- lp, finallyBreaking = c.scanStatements(v.Finally.List)
- }
- if finallyBreaking != nil {
- c.block.breaking = finallyBreaking
- if lp == -1 {
- bodyNeedResult = finallyBreaking.needResult
- }
- } else {
- bodyNeedResult = needResult
- }
- lbl := len(c.p.code)
- c.emit(nil)
- if needResult {
- c.emit(clearResult)
- }
- c.compileBlockStatement(v.Body, bodyNeedResult)
- var catchOffset int
- if v.Catch != nil {
- lbl2 := len(c.p.code) // jump over the catch block
- c.emit(nil)
- catchOffset = len(c.p.code) - lbl
- if v.Catch.Parameter != nil {
- c.block = &block{
- typ: blockScope,
- outer: c.block,
- }
- c.newBlockScope()
- list := v.Catch.Body.List
- funcs := c.extractFunctions(list)
- if _, ok := v.Catch.Parameter.(ast.Pattern); ok {
- // add anonymous binding for the catch parameter, note it must be first
- c.scope.addBinding(int(v.Catch.Idx0()) - 1)
- }
- c.createBindings(v.Catch.Parameter, func(name unistring.String, offset int) {
- if c.scope.strict {
- switch name {
- case "arguments", "eval":
- c.throwSyntaxError(offset, "Catch variable may not be eval or arguments in strict mode")
- }
- }
- c.scope.bindNameLexical(name, true, offset)
- })
- enter := &enterBlock{}
- c.emit(enter)
- if pattern, ok := v.Catch.Parameter.(ast.Pattern); ok {
- c.scope.bindings[0].emitGet()
- c.emitPattern(pattern, func(target, init compiledExpr) {
- c.emitPatternLexicalAssign(target, init)
- }, false)
- }
- for _, decl := range funcs {
- c.scope.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
- }
- c.compileLexicalDeclarations(list, true)
- c.compileFunctions(funcs)
- c.compileStatements(list, bodyNeedResult)
- c.leaveScopeBlock(enter)
- if c.scope.dynLookup || c.scope.bindings[0].inStash {
- c.p.code[lbl+catchOffset] = &enterCatchBlock{
- names: enter.names,
- stashSize: enter.stashSize,
- stackSize: enter.stackSize,
- }
- } else {
- enter.stackSize--
- }
- c.popScope()
- } else {
- c.emit(pop)
- c.compileBlockStatement(v.Catch.Body, bodyNeedResult)
- }
- c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
- }
- var finallyOffset int
- if v.Finally != nil {
- c.emit(enterFinally{})
- finallyOffset = len(c.p.code) - lbl // finallyOffset should not include enterFinally
- if bodyNeedResult && finallyBreaking != nil && lp == -1 {
- c.emit(clearResult)
- }
- c.compileBlockStatement(v.Finally, false)
- c.emit(leaveFinally{})
- } else {
- c.emit(leaveTry{})
- }
- c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset)}
- c.leaveBlock()
- }
- func (c *compiler) addSrcMap(node ast.Node) {
- c.p.addSrcMap(int(node.Idx0()) - 1)
- }
- func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
- c.compileExpression(v.Argument).emitGetter(true)
- c.addSrcMap(v)
- 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 unistring.String) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- start := len(c.p.code)
- c.compileStatement(v.Body, needResult)
- c.block.cont = len(c.p.code)
- c.emitExpr(c.compileExpression(v.Test), true)
- c.emit(jeqP(start - len(c.p.code)))
- c.leaveBlock()
- }
- func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
- c.compileLabeledForStatement(v, needResult, "")
- }
- func (c *compiler) compileForHeadLexDecl(decl *ast.LexicalDeclaration, needResult bool) *enterBlock {
- c.block = &block{
- typ: blockIterScope,
- outer: c.block,
- needResult: needResult,
- }
- c.newBlockScope()
- enterIterBlock := &enterBlock{}
- c.emit(enterIterBlock)
- c.createLexicalBindings(decl)
- c.compileLexicalDeclaration(decl)
- return enterIterBlock
- }
- func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label unistring.String) {
- loopBlock := &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- c.block = loopBlock
- var enterIterBlock *enterBlock
- switch init := v.Initializer.(type) {
- case nil:
- // no-op
- case *ast.ForLoopInitializerLexicalDecl:
- enterIterBlock = c.compileForHeadLexDecl(&init.LexicalDeclaration, needResult)
- case *ast.ForLoopInitializerVarDeclList:
- for _, expr := range init.List {
- c.compileVarBinding(expr)
- }
- case *ast.ForLoopInitializerExpression:
- c.compileExpression(init.Expression).emitGetter(false)
- default:
- c.assert(false, int(v.For)-1, "Unsupported for loop initializer: %T", init)
- panic("unreachable")
- }
- if needResult {
- c.emit(clearResult) // initial result
- }
- if enterIterBlock != nil {
- c.emit(jump(1))
- }
- start := len(c.p.code)
- 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 {
- leave := c.enterDummyMode()
- c.compileStatement(v.Body, false)
- if v.Update != nil {
- c.compileExpression(v.Update).emitGetter(false)
- }
- leave()
- 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(clearResult)
- }
- c.compileStatement(v.Body, needResult)
- loopBlock.cont = len(c.p.code)
- if enterIterBlock != nil {
- c.emit(jump(1))
- }
- if v.Update != nil {
- c.compileExpression(v.Update).emitGetter(false)
- }
- if enterIterBlock != nil {
- if c.scope.needStash || c.scope.isDynamic() {
- c.p.code[start-1] = copyStash{}
- c.p.code[loopBlock.cont] = copyStash{}
- } else {
- if l := len(c.p.code); l > loopBlock.cont {
- loopBlock.cont++
- } else {
- c.p.code = c.p.code[:l-1]
- }
- }
- }
- c.emit(jump(start - len(c.p.code)))
- if v.Test != nil {
- if !testConst {
- c.p.code[j] = jneP(len(c.p.code) - j)
- }
- }
- end:
- if enterIterBlock != nil {
- c.leaveScopeBlock(enterIterBlock)
- c.popScope()
- }
- c.leaveBlock()
- }
- func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
- c.compileLabeledForInStatement(v, needResult, "")
- }
- func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *enterBlock) {
- switch into := into.(type) {
- case *ast.ForIntoExpression:
- c.compileExpression(into.Expression).emitSetter(&c.enumGetExpr, false)
- case *ast.ForIntoVar:
- if c.scope.strict && into.Binding.Initializer != nil {
- c.throwSyntaxError(int(into.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.")
- }
- switch target := into.Binding.Target.(type) {
- case *ast.Identifier:
- c.compileIdentifierExpression(target).emitSetter(&c.enumGetExpr, false)
- case ast.Pattern:
- c.emit(enumGet)
- c.emitPattern(target, c.emitPatternVarAssign, false)
- default:
- c.throwSyntaxError(int(target.Idx0()-1), "unsupported for-in var target: %T", target)
- }
- case *ast.ForDeclaration:
- c.block = &block{
- typ: blockIterScope,
- outer: c.block,
- needResult: needResult,
- }
- c.newBlockScope()
- enter = &enterBlock{}
- c.emit(enter)
- switch target := into.Target.(type) {
- case *ast.Identifier:
- b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1)
- c.emit(enumGet)
- b.emitInitP()
- case ast.Pattern:
- c.createLexicalBinding(target, into.IsConst)
- c.emit(enumGet)
- c.emitPattern(target, func(target, init compiledExpr) {
- c.emitPatternLexicalAssign(target, init)
- }, false)
- default:
- c.assert(false, int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target)
- }
- default:
- c.assert(false, int(into.Idx0())-1, "Unsupported for-into: %T", into)
- panic("unreachable")
- }
- return
- }
- func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.Expression, body ast.Statement, iter, needResult bool, label unistring.String) {
- c.block = &block{
- typ: blockLoopEnum,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- enterPos := -1
- if forDecl, ok := into.(*ast.ForDeclaration); ok {
- c.block = &block{
- typ: blockScope,
- outer: c.block,
- needResult: false,
- }
- c.newBlockScope()
- enterPos = len(c.p.code)
- c.emit(jump(1))
- c.createLexicalBinding(forDecl.Target, forDecl.IsConst)
- }
- c.compileExpression(source).emitGetter(true)
- if enterPos != -1 {
- s := c.scope
- used := len(c.block.breaks) > 0 || s.isDynamic()
- if !used {
- for _, b := range s.bindings {
- if b.useCount() > 0 {
- used = true
- break
- }
- }
- }
- if used {
- // We need the stack untouched because it contains the source.
- // This is not the most optimal way, but it's an edge case, hopefully quite rare.
- for _, b := range s.bindings {
- b.moveToStash()
- }
- enter := &enterBlock{}
- c.p.code[enterPos] = enter
- c.leaveScopeBlock(enter)
- } else {
- c.block = c.block.outer
- }
- c.popScope()
- }
- if iter {
- c.emit(iterateP)
- } else {
- c.emit(enumerate)
- }
- if needResult {
- c.emit(clearResult)
- }
- start := len(c.p.code)
- c.block.cont = start
- c.emit(nil)
- enterIterBlock := c.compileForInto(into, needResult)
- if needResult {
- c.emit(clearResult)
- }
- c.compileStatement(body, needResult)
- if enterIterBlock != nil {
- c.leaveScopeBlock(enterIterBlock)
- c.popScope()
- }
- c.emit(jump(start - len(c.p.code)))
- if iter {
- c.p.code[start] = iterNext(len(c.p.code) - start)
- } else {
- c.p.code[start] = enumNext(len(c.p.code) - start)
- }
- c.emit(enumPop, jump(2))
- c.leaveBlock()
- c.emit(enumPopClose)
- }
- func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) {
- c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, false, needResult, label)
- }
- func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) {
- c.compileLabeledForOfStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label unistring.String) {
- c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, true, needResult, label)
- }
- func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
- c.compileLabeledWhileStatement(v, needResult, "")
- }
- func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label unistring.String) {
- c.block = &block{
- typ: blockLoop,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- if needResult {
- c.emit(clearResult)
- }
- start := len(c.p.code)
- 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 {
- c.compileStatementDummy(v.Body)
- 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(clearResult)
- }
- c.compileStatement(v.Body, needResult)
- c.emit(jump(start - len(c.p.code)))
- if !testTrue {
- c.p.code[j] = jneP(len(c.p.code) - j)
- }
- end:
- c.leaveBlock()
- }
- func (c *compiler) compileEmptyStatement(needResult bool) {
- if needResult {
- c.emit(clearResult)
- }
- }
- func (c *compiler) compileBranchStatement(v *ast.BranchStatement) {
- switch v.Token {
- case token.BREAK:
- c.compileBreak(v.Label, v.Idx)
- case token.CONTINUE:
- c.compileContinue(v.Label, v.Idx)
- default:
- c.assert(false, int(v.Idx0())-1, "Unknown branch statement token: %s", v.Token.String())
- panic("unreachable")
- }
- }
- func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
- switch st.Token {
- case token.BREAK:
- return c.findBreakBlock(st.Label, true)
- case token.CONTINUE:
- return c.findBreakBlock(st.Label, false)
- }
- return nil
- }
- func (c *compiler) findBreakBlock(label *ast.Identifier, isBreak bool) (res *block) {
- if label != nil {
- var found *block
- for b := c.block; b != nil; b = b.outer {
- if res == nil {
- if bb := b.breaking; bb != nil {
- res = bb
- if isBreak {
- return
- }
- }
- }
- if b.label == label.Name {
- found = b
- break
- }
- }
- if !isBreak && found != nil && found.typ != blockLoop && found.typ != blockLoopEnum {
- c.throwSyntaxError(int(label.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", label.Name)
- }
- if res == nil {
- res = found
- }
- } else {
- // find the nearest loop or switch (if break)
- L:
- for b := c.block; b != nil; b = b.outer {
- if bb := b.breaking; bb != nil {
- return bb
- }
- switch b.typ {
- case blockLoop, blockLoopEnum:
- res = b
- break L
- case blockSwitch:
- if isBreak {
- res = b
- break L
- }
- }
- }
- }
- return
- }
- func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBreak bool) *block {
- block := c.findBreakBlock(label, isBreak)
- if block == nil {
- c.throwSyntaxError(int(idx)-1, "Could not find block")
- panic("unreachable")
- }
- contForLoop := !isBreak && block.typ == blockLoop
- L:
- for b := c.block; b != block; b = b.outer {
- switch b.typ {
- case blockIterScope:
- // blockIterScope in 'for' loops is shared across iterations, so
- // continue should not pop it.
- if contForLoop && b.outer == block {
- break L
- }
- fallthrough
- case blockScope:
- b.breaks = append(b.breaks, len(c.p.code))
- c.emit(nil)
- case blockTry:
- c.emit(leaveTry{})
- case blockWith:
- c.emit(leaveWith)
- case blockLoopEnum:
- c.emit(enumPopClose)
- }
- }
- return block
- }
- func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
- block := c.emitBlockExitCode(label, idx, true)
- block.breaks = append(block.breaks, len(c.p.code))
- c.emit(nil)
- }
- func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
- block := c.emitBlockExitCode(label, idx, false)
- block.conts = append(block.conts, len(c.p.code))
- c.emit(nil)
- }
- func (c *compiler) compileIfBody(s ast.Statement, needResult bool) {
- if !c.scope.strict {
- if s, ok := s.(*ast.FunctionDeclaration); ok && !s.Function.Async && !s.Function.Generator {
- c.compileFunction(s)
- if needResult {
- c.emit(clearResult)
- }
- return
- }
- }
- c.compileStatement(s, needResult)
- }
- func (c *compiler) compileIfBodyDummy(s ast.Statement) {
- leave := c.enterDummyMode()
- defer leave()
- c.compileIfBody(s, false)
- }
- func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
- test := c.compileExpression(v.Test)
- if needResult {
- c.emit(clearResult)
- }
- if test.constant() {
- r, ex := c.evalConst(test)
- if ex != nil {
- test.addSrcMap()
- c.emitThrow(ex.val)
- return
- }
- if r.ToBoolean() {
- c.compileIfBody(v.Consequent, needResult)
- if v.Alternate != nil {
- c.compileIfBodyDummy(v.Alternate)
- }
- } else {
- c.compileIfBodyDummy(v.Consequent)
- if v.Alternate != nil {
- c.compileIfBody(v.Alternate, needResult)
- } else {
- if needResult {
- c.emit(clearResult)
- }
- }
- }
- return
- }
- test.emitGetter(true)
- jmp := len(c.p.code)
- c.emit(nil)
- c.compileIfBody(v.Consequent, needResult)
- if v.Alternate != nil {
- jmp1 := len(c.p.code)
- c.emit(nil)
- c.p.code[jmp] = jneP(len(c.p.code) - jmp)
- c.compileIfBody(v.Alternate, needResult)
- c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
- } else {
- if needResult {
- c.emit(jump(2))
- c.p.code[jmp] = jneP(len(c.p.code) - jmp)
- c.emit(clearResult)
- } else {
- c.p.code[jmp] = jneP(len(c.p.code) - jmp)
- }
- }
- }
- func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
- if s := c.scope.nearestFunction(); s != nil && s.funcType == funcClsInit {
- c.throwSyntaxError(int(v.Return)-1, "Illegal return statement")
- }
- if v.Argument != nil {
- c.emitExpr(c.compileExpression(v.Argument), true)
- } else {
- c.emit(loadUndef)
- }
- for b := c.block; b != nil; b = b.outer {
- switch b.typ {
- case blockTry:
- c.emit(saveResult, leaveTry{}, loadResult)
- case blockLoopEnum:
- c.emit(enumPopClose)
- }
- }
- if s := c.scope.nearestFunction(); s != nil && s.funcType == funcDerivedCtor {
- b := s.boundNames[thisBindingName]
- c.assert(b != nil, int(v.Return)-1, "Derived constructor, but no 'this' binding")
- b.markAccessPoint()
- }
- c.emit(ret)
- }
- func (c *compiler) checkVarConflict(name unistring.String, offset int) {
- for sc := c.scope; sc != nil; sc = sc.outer {
- if b, exists := sc.boundNames[name]; exists && !b.isVar && !(b.isArg && sc != c.scope) {
- c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
- }
- if sc.isFunction() {
- break
- }
- }
- }
- func (c *compiler) emitVarAssign(name unistring.String, offset int, init compiledExpr) {
- c.checkVarConflict(name, offset)
- if init != nil {
- b, noDyn := c.scope.lookupName(name)
- if noDyn {
- c.emitNamedOrConst(init, name)
- c.p.addSrcMap(offset)
- b.emitInitP()
- } else {
- c.emitVarRef(name, offset, b)
- c.emitNamedOrConst(init, name)
- c.p.addSrcMap(offset)
- c.emit(initValueP)
- }
- }
- }
- func (c *compiler) compileVarBinding(expr *ast.Binding) {
- switch target := expr.Target.(type) {
- case *ast.Identifier:
- c.emitVarAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer))
- case ast.Pattern:
- c.compileExpression(expr.Initializer).emitGetter(true)
- c.emitPattern(target, c.emitPatternVarAssign, false)
- default:
- c.throwSyntaxError(int(target.Idx0()-1), "unsupported variable binding target: %T", target)
- }
- }
- func (c *compiler) emitLexicalAssign(name unistring.String, offset int, init compiledExpr) {
- b := c.scope.boundNames[name]
- c.assert(b != nil, offset, "Lexical declaration for an unbound name")
- if init != nil {
- c.emitNamedOrConst(init, name)
- c.p.addSrcMap(offset)
- } else {
- if b.isConst {
- c.throwSyntaxError(offset, "Missing initializer in const declaration")
- }
- c.emit(loadUndef)
- }
- b.emitInitP()
- }
- func (c *compiler) emitPatternVarAssign(target, init compiledExpr) {
- id := target.(*compiledIdentifierExpr)
- c.emitVarAssign(id.name, id.offset, init)
- }
- func (c *compiler) emitPatternLexicalAssign(target, init compiledExpr) {
- id := target.(*compiledIdentifierExpr)
- c.emitLexicalAssign(id.name, id.offset, init)
- }
- func (c *compiler) emitPatternAssign(target, init compiledExpr) {
- if id, ok := target.(*compiledIdentifierExpr); ok {
- b, noDyn := c.scope.lookupName(id.name)
- if noDyn {
- c.emitNamedOrConst(init, id.name)
- b.emitSetP()
- } else {
- c.emitVarRef(id.name, id.offset, b)
- c.emitNamedOrConst(init, id.name)
- c.emit(putValueP)
- }
- } else {
- target.emitRef()
- c.emitExpr(init, true)
- c.emit(putValueP)
- }
- }
- func (c *compiler) compileLexicalBinding(expr *ast.Binding) {
- switch target := expr.Target.(type) {
- case *ast.Identifier:
- c.emitLexicalAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer))
- case ast.Pattern:
- c.compileExpression(expr.Initializer).emitGetter(true)
- c.emitPattern(target, func(target, init compiledExpr) {
- c.emitPatternLexicalAssign(target, init)
- }, false)
- default:
- c.throwSyntaxError(int(target.Idx0()-1), "unsupported lexical binding target: %T", target)
- }
- }
- func (c *compiler) compileVariableStatement(v *ast.VariableStatement) {
- for _, expr := range v.List {
- c.compileVarBinding(expr)
- }
- }
- func (c *compiler) compileLexicalDeclaration(v *ast.LexicalDeclaration) {
- for _, e := range v.List {
- c.compileLexicalBinding(e)
- }
- }
- func (c *compiler) isEmptyResult(st ast.Statement) bool {
- switch st := st.(type) {
- case *ast.EmptyStatement, *ast.VariableStatement, *ast.LexicalDeclaration, *ast.FunctionDeclaration,
- *ast.ClassDeclaration, *ast.BranchStatement, *ast.DebuggerStatement:
- return true
- case *ast.LabelledStatement:
- return c.isEmptyResult(st.Statement)
- case *ast.BlockStatement:
- for _, s := range st.List {
- if _, ok := s.(*ast.BranchStatement); ok {
- return true
- }
- if !c.isEmptyResult(s) {
- return false
- }
- }
- return true
- }
- return false
- }
- func (c *compiler) scanStatements(list []ast.Statement) (lastProducingIdx int, breakingBlock *block) {
- lastProducingIdx = -1
- for i, st := range list {
- if bs, ok := st.(*ast.BranchStatement); ok {
- if blk := c.findBranchBlock(bs); blk != nil {
- breakingBlock = blk
- }
- break
- }
- if !c.isEmptyResult(st) {
- lastProducingIdx = i
- }
- }
- return
- }
- func (c *compiler) compileStatementsNeedResult(list []ast.Statement, lastProducingIdx int) {
- if lastProducingIdx >= 0 {
- for _, st := range list[:lastProducingIdx] {
- if _, ok := st.(*ast.FunctionDeclaration); ok {
- continue
- }
- c.compileStatement(st, false)
- }
- c.compileStatement(list[lastProducingIdx], true)
- }
- var leave func()
- defer func() {
- if leave != nil {
- leave()
- }
- }()
- for _, st := range list[lastProducingIdx+1:] {
- if _, ok := st.(*ast.FunctionDeclaration); ok {
- continue
- }
- c.compileStatement(st, false)
- if leave == nil {
- if _, ok := st.(*ast.BranchStatement); ok {
- leave = c.enterDummyMode()
- }
- }
- }
- }
- func (c *compiler) compileStatements(list []ast.Statement, needResult bool) {
- lastProducingIdx, blk := c.scanStatements(list)
- if blk != nil {
- needResult = blk.needResult
- }
- if needResult {
- c.compileStatementsNeedResult(list, lastProducingIdx)
- return
- }
- for _, st := range list {
- if _, ok := st.(*ast.FunctionDeclaration); ok {
- continue
- }
- c.compileStatement(st, false)
- }
- }
- func (c *compiler) compileGenericLabeledStatement(v ast.Statement, needResult bool, label unistring.String) {
- c.block = &block{
- typ: blockLabel,
- outer: c.block,
- label: label,
- needResult: needResult,
- }
- c.compileStatement(v, needResult)
- c.leaveBlock()
- }
- func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) {
- var scopeDeclared bool
- funcs := c.extractFunctions(v.List)
- if len(funcs) > 0 {
- c.newBlockScope()
- scopeDeclared = true
- }
- c.createFunctionBindings(funcs)
- scopeDeclared = c.compileLexicalDeclarations(v.List, scopeDeclared)
- var enter *enterBlock
- if scopeDeclared {
- c.block = &block{
- outer: c.block,
- typ: blockScope,
- needResult: needResult,
- }
- enter = &enterBlock{}
- c.emit(enter)
- }
- c.compileFunctions(funcs)
- c.compileStatements(v.List, needResult)
- if scopeDeclared {
- c.leaveScopeBlock(enter)
- c.popScope()
- }
- }
- func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) {
- c.emitExpr(c.compileExpression(v.Expression), needResult)
- if needResult {
- c.emit(saveResult)
- }
- }
- 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.newBlockScope()
- c.scope.dynamic = 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)
- var funcs []*ast.FunctionDeclaration
- for _, s := range v.Body {
- f := c.extractFunctions(s.Consequent)
- funcs = append(funcs, f...)
- }
- var scopeDeclared bool
- if len(funcs) > 0 {
- c.newBlockScope()
- scopeDeclared = true
- c.createFunctionBindings(funcs)
- }
- for _, s := range v.Body {
- scopeDeclared = c.compileLexicalDeclarations(s.Consequent, scopeDeclared)
- }
- var enter *enterBlock
- var db *binding
- if scopeDeclared {
- c.block = &block{
- typ: blockScope,
- outer: c.block,
- needResult: needResult,
- }
- enter = &enterBlock{}
- c.emit(enter)
- // create anonymous variable for the discriminant
- bindings := c.scope.bindings
- var bb []*binding
- if cap(bindings) == len(bindings) {
- bb = make([]*binding, len(bindings)+1)
- } else {
- bb = bindings[:len(bindings)+1]
- }
- copy(bb[1:], bindings)
- db = &binding{
- scope: c.scope,
- isConst: true,
- isStrict: true,
- }
- bb[0] = db
- c.scope.bindings = bb
- }
- c.compileFunctions(funcs)
- if needResult {
- c.emit(clearResult)
- }
- jumps := make([]int, len(v.Body))
- for i, s := range v.Body {
- if s.Test != nil {
- if db != nil {
- db.emitGet()
- } else {
- c.emit(dup)
- }
- c.compileExpression(s.Test).emitGetter(true)
- c.emit(op_strict_eq)
- if db != nil {
- c.emit(jneP(2))
- } else {
- c.emit(jneP(3), pop)
- }
- jumps[i] = len(c.p.code)
- c.emit(nil)
- }
- }
- if db == 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.compileStatements(s.Consequent, needResult)
- }
- if jumpNoMatch != -1 {
- c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch)
- }
- if enter != nil {
- c.leaveScopeBlock(enter)
- enter.stackSize--
- c.popScope()
- }
- c.leaveBlock()
- }
- func (c *compiler) compileClassDeclaration(v *ast.ClassDeclaration) {
- c.emitLexicalAssign(v.Class.Name.Name, int(v.Class.Class)-1, c.compileClassLiteral(v.Class, false))
- }
|