1package goja 2 3import ( 4 "fmt" 5 "github.com/dop251/goja/ast" 6 "github.com/dop251/goja/file" 7 "github.com/dop251/goja/token" 8 "github.com/dop251/goja/unistring" 9) 10 11func (c *compiler) compileStatement(v ast.Statement, needResult bool) { 12 // log.Printf("compileStatement(): %T", v) 13 14 switch v := v.(type) { 15 case *ast.BlockStatement: 16 c.compileBlockStatement(v, needResult) 17 case *ast.ExpressionStatement: 18 c.compileExpressionStatement(v, needResult) 19 case *ast.VariableStatement: 20 c.compileVariableStatement(v) 21 case *ast.LexicalDeclaration: 22 c.compileLexicalDeclaration(v) 23 case *ast.ReturnStatement: 24 c.compileReturnStatement(v) 25 case *ast.IfStatement: 26 c.compileIfStatement(v, needResult) 27 case *ast.DoWhileStatement: 28 c.compileDoWhileStatement(v, needResult) 29 case *ast.ForStatement: 30 c.compileForStatement(v, needResult) 31 case *ast.ForInStatement: 32 c.compileForInStatement(v, needResult) 33 case *ast.ForOfStatement: 34 c.compileForOfStatement(v, needResult) 35 case *ast.WhileStatement: 36 c.compileWhileStatement(v, needResult) 37 case *ast.BranchStatement: 38 c.compileBranchStatement(v) 39 case *ast.TryStatement: 40 c.compileTryStatement(v, needResult) 41 case *ast.ThrowStatement: 42 c.compileThrowStatement(v) 43 case *ast.SwitchStatement: 44 c.compileSwitchStatement(v, needResult) 45 case *ast.LabelledStatement: 46 c.compileLabeledStatement(v, needResult) 47 case *ast.EmptyStatement: 48 c.compileEmptyStatement(needResult) 49 case *ast.FunctionDeclaration: 50 c.compileStandaloneFunctionDecl(v) 51 // note functions inside blocks are hoisted to the top of the block and are compiled using compileFunctions() 52 case *ast.WithStatement: 53 c.compileWithStatement(v, needResult) 54 case *ast.DebuggerStatement: 55 default: 56 panic(fmt.Errorf("Unknown statement type: %T", v)) 57 } 58} 59 60func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) { 61 label := v.Label.Name 62 if c.scope.strict { 63 c.checkIdentifierName(label, int(v.Label.Idx)-1) 64 } 65 for b := c.block; b != nil; b = b.outer { 66 if b.label == label { 67 c.throwSyntaxError(int(v.Label.Idx-1), "Label '%s' has already been declared", label) 68 } 69 } 70 switch s := v.Statement.(type) { 71 case *ast.ForInStatement: 72 c.compileLabeledForInStatement(s, needResult, label) 73 case *ast.ForOfStatement: 74 c.compileLabeledForOfStatement(s, needResult, label) 75 case *ast.ForStatement: 76 c.compileLabeledForStatement(s, needResult, label) 77 case *ast.WhileStatement: 78 c.compileLabeledWhileStatement(s, needResult, label) 79 case *ast.DoWhileStatement: 80 c.compileLabeledDoWhileStatement(s, needResult, label) 81 default: 82 c.compileGenericLabeledStatement(s, needResult, label) 83 } 84} 85 86func (c *compiler) updateEnterBlock(enter *enterBlock) { 87 scope := c.scope 88 stashSize, stackSize := 0, 0 89 if scope.dynLookup { 90 stashSize = len(scope.bindings) 91 enter.names = scope.makeNamesMap() 92 } else { 93 for _, b := range scope.bindings { 94 if b.inStash { 95 stashSize++ 96 } else { 97 stackSize++ 98 } 99 } 100 } 101 enter.stashSize, enter.stackSize = uint32(stashSize), uint32(stackSize) 102} 103 104func (c *compiler) compileTryStatement(v *ast.TryStatement, needResult bool) { 105 c.block = &block{ 106 typ: blockTry, 107 outer: c.block, 108 } 109 var lp int 110 var bodyNeedResult bool 111 var finallyBreaking *block 112 if v.Finally != nil { 113 lp, finallyBreaking = c.scanStatements(v.Finally.List) 114 } 115 if finallyBreaking != nil { 116 c.block.breaking = finallyBreaking 117 if lp == -1 { 118 bodyNeedResult = finallyBreaking.needResult 119 } 120 } else { 121 bodyNeedResult = needResult 122 } 123 lbl := len(c.p.code) 124 c.emit(nil) 125 if needResult { 126 c.emit(clearResult) 127 } 128 c.compileBlockStatement(v.Body, bodyNeedResult) 129 c.emit(halt) 130 lbl2 := len(c.p.code) 131 c.emit(nil) 132 var catchOffset int 133 if v.Catch != nil { 134 catchOffset = len(c.p.code) - lbl 135 if v.Catch.Parameter != nil { 136 c.block = &block{ 137 typ: blockScope, 138 outer: c.block, 139 } 140 c.newBlockScope() 141 list := v.Catch.Body.List 142 funcs := c.extractFunctions(list) 143 if _, ok := v.Catch.Parameter.(ast.Pattern); ok { 144 // add anonymous binding for the catch parameter, note it must be first 145 c.scope.addBinding(int(v.Catch.Idx0()) - 1) 146 } 147 c.createBindings(v.Catch.Parameter, func(name unistring.String, offset int) { 148 if c.scope.strict { 149 switch name { 150 case "arguments", "eval": 151 c.throwSyntaxError(offset, "Catch variable may not be eval or arguments in strict mode") 152 } 153 } 154 c.scope.bindNameLexical(name, true, offset) 155 }) 156 enter := &enterBlock{} 157 c.emit(enter) 158 if pattern, ok := v.Catch.Parameter.(ast.Pattern); ok { 159 c.scope.bindings[0].emitGet() 160 c.emitPattern(pattern, func(target, init compiledExpr) { 161 c.emitPatternLexicalAssign(target, init, false) 162 }, false) 163 } 164 for _, decl := range funcs { 165 c.scope.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1) 166 } 167 c.compileLexicalDeclarations(list, true) 168 c.compileFunctions(funcs) 169 c.compileStatements(list, bodyNeedResult) 170 c.leaveScopeBlock(enter) 171 if c.scope.dynLookup || c.scope.bindings[0].inStash { 172 c.p.code[lbl+catchOffset] = &enterCatchBlock{ 173 names: enter.names, 174 stashSize: enter.stashSize, 175 stackSize: enter.stackSize, 176 } 177 } else { 178 enter.stackSize-- 179 } 180 c.popScope() 181 } else { 182 c.emit(pop) 183 c.compileBlockStatement(v.Catch.Body, bodyNeedResult) 184 } 185 c.emit(halt) 186 } 187 var finallyOffset int 188 if v.Finally != nil { 189 lbl1 := len(c.p.code) 190 c.emit(nil) 191 finallyOffset = len(c.p.code) - lbl 192 c.compileBlockStatement(v.Finally, false) 193 c.emit(halt, retFinally) 194 195 c.p.code[lbl1] = jump(len(c.p.code) - lbl1) 196 } 197 c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset)} 198 c.p.code[lbl2] = jump(len(c.p.code) - lbl2) 199 c.leaveBlock() 200} 201 202func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) { 203 //c.p.srcMap = append(c.p.srcMap, srcMapItem{pc: len(c.p.code), srcPos: int(v.Throw) - 1}) 204 c.compileExpression(v.Argument).emitGetter(true) 205 c.emit(throw) 206} 207 208func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) { 209 c.compileLabeledDoWhileStatement(v, needResult, "") 210} 211 212func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label unistring.String) { 213 c.block = &block{ 214 typ: blockLoop, 215 outer: c.block, 216 label: label, 217 needResult: needResult, 218 } 219 220 start := len(c.p.code) 221 c.compileStatement(v.Body, needResult) 222 c.block.cont = len(c.p.code) 223 c.emitExpr(c.compileExpression(v.Test), true) 224 c.emit(jeq(start - len(c.p.code))) 225 c.leaveBlock() 226} 227 228func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) { 229 c.compileLabeledForStatement(v, needResult, "") 230} 231 232func (c *compiler) compileForHeadLexDecl(decl *ast.LexicalDeclaration, needResult bool) *enterBlock { 233 c.block = &block{ 234 typ: blockIterScope, 235 outer: c.block, 236 needResult: needResult, 237 } 238 239 c.newBlockScope() 240 enterIterBlock := &enterBlock{} 241 c.emit(enterIterBlock) 242 c.createLexicalBindings(decl) 243 c.compileLexicalDeclaration(decl) 244 return enterIterBlock 245} 246 247func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label unistring.String) { 248 loopBlock := &block{ 249 typ: blockLoop, 250 outer: c.block, 251 label: label, 252 needResult: needResult, 253 } 254 c.block = loopBlock 255 256 var enterIterBlock *enterBlock 257 switch init := v.Initializer.(type) { 258 case nil: 259 // no-op 260 case *ast.ForLoopInitializerLexicalDecl: 261 enterIterBlock = c.compileForHeadLexDecl(&init.LexicalDeclaration, needResult) 262 case *ast.ForLoopInitializerVarDeclList: 263 for _, expr := range init.List { 264 c.compileVarBinding(expr) 265 } 266 case *ast.ForLoopInitializerExpression: 267 c.compileExpression(init.Expression).emitGetter(false) 268 default: 269 panic(fmt.Sprintf("Unsupported for loop initializer: %T", init)) 270 } 271 272 if needResult { 273 c.emit(clearResult) // initial result 274 } 275 276 if enterIterBlock != nil { 277 c.emit(jump(1)) 278 } 279 280 start := len(c.p.code) 281 var j int 282 testConst := false 283 if v.Test != nil { 284 expr := c.compileExpression(v.Test) 285 if expr.constant() { 286 r, ex := c.evalConst(expr) 287 if ex == nil { 288 if r.ToBoolean() { 289 testConst = true 290 } else { 291 leave := c.enterDummyMode() 292 c.compileStatement(v.Body, false) 293 if v.Update != nil { 294 c.compileExpression(v.Update).emitGetter(false) 295 } 296 leave() 297 goto end 298 } 299 } else { 300 expr.addSrcMap() 301 c.emitThrow(ex.val) 302 goto end 303 } 304 } else { 305 expr.emitGetter(true) 306 j = len(c.p.code) 307 c.emit(nil) 308 } 309 } 310 if needResult { 311 c.emit(clearResult) 312 } 313 c.compileStatement(v.Body, needResult) 314 loopBlock.cont = len(c.p.code) 315 if enterIterBlock != nil { 316 c.emit(jump(1)) 317 } 318 if v.Update != nil { 319 c.compileExpression(v.Update).emitGetter(false) 320 } 321 if enterIterBlock != nil { 322 if c.scope.needStash || c.scope.isDynamic() { 323 c.p.code[start-1] = copyStash{} 324 c.p.code[loopBlock.cont] = copyStash{} 325 } else { 326 if l := len(c.p.code); l > loopBlock.cont { 327 loopBlock.cont++ 328 } else { 329 c.p.code = c.p.code[:l-1] 330 } 331 } 332 } 333 c.emit(jump(start - len(c.p.code))) 334 if v.Test != nil { 335 if !testConst { 336 c.p.code[j] = jne(len(c.p.code) - j) 337 } 338 } 339end: 340 if enterIterBlock != nil { 341 c.leaveScopeBlock(enterIterBlock) 342 c.popScope() 343 } 344 c.leaveBlock() 345} 346 347func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) { 348 c.compileLabeledForInStatement(v, needResult, "") 349} 350 351func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *enterBlock) { 352 switch into := into.(type) { 353 case *ast.ForIntoExpression: 354 c.compileExpression(into.Expression).emitSetter(&c.enumGetExpr, false) 355 case *ast.ForIntoVar: 356 if c.scope.strict && into.Binding.Initializer != nil { 357 c.throwSyntaxError(int(into.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.") 358 } 359 switch target := into.Binding.Target.(type) { 360 case *ast.Identifier: 361 c.compileIdentifierExpression(target).emitSetter(&c.enumGetExpr, false) 362 case ast.Pattern: 363 c.emit(enumGet) 364 c.emitPattern(target, c.emitPatternVarAssign, false) 365 default: 366 c.throwSyntaxError(int(target.Idx0()-1), "unsupported for-in var target: %T", target) 367 } 368 case *ast.ForDeclaration: 369 370 c.block = &block{ 371 typ: blockIterScope, 372 outer: c.block, 373 needResult: needResult, 374 } 375 376 c.newBlockScope() 377 enter = &enterBlock{} 378 c.emit(enter) 379 switch target := into.Target.(type) { 380 case *ast.Identifier: 381 b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1) 382 c.emit(enumGet) 383 b.emitInit() 384 case ast.Pattern: 385 c.createLexicalBinding(target, into.IsConst) 386 c.emit(enumGet) 387 c.emitPattern(target, func(target, init compiledExpr) { 388 c.emitPatternLexicalAssign(target, init, into.IsConst) 389 }, false) 390 default: 391 c.throwSyntaxError(int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target) 392 } 393 default: 394 panic(fmt.Sprintf("Unsupported for-into: %T", into)) 395 } 396 397 return 398} 399 400func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.Expression, body ast.Statement, iter, needResult bool, label unistring.String) { 401 c.block = &block{ 402 typ: blockLoopEnum, 403 outer: c.block, 404 label: label, 405 needResult: needResult, 406 } 407 enterPos := -1 408 if forDecl, ok := into.(*ast.ForDeclaration); ok { 409 c.block = &block{ 410 typ: blockScope, 411 outer: c.block, 412 needResult: false, 413 } 414 c.newBlockScope() 415 enterPos = len(c.p.code) 416 c.emit(jump(1)) 417 c.createLexicalBinding(forDecl.Target, forDecl.IsConst) 418 } 419 c.compileExpression(source).emitGetter(true) 420 if enterPos != -1 { 421 s := c.scope 422 used := len(c.block.breaks) > 0 || s.isDynamic() 423 if !used { 424 for _, b := range s.bindings { 425 if b.useCount() > 0 { 426 used = true 427 break 428 } 429 } 430 } 431 if used { 432 // We need the stack untouched because it contains the source. 433 // This is not the most optimal way, but it's an edge case, hopefully quite rare. 434 for _, b := range s.bindings { 435 b.moveToStash() 436 } 437 enter := &enterBlock{} 438 c.p.code[enterPos] = enter 439 c.leaveScopeBlock(enter) 440 } else { 441 c.block = c.block.outer 442 } 443 c.popScope() 444 } 445 if iter { 446 c.emit(iterateP) 447 } else { 448 c.emit(enumerate) 449 } 450 if needResult { 451 c.emit(clearResult) 452 } 453 start := len(c.p.code) 454 c.block.cont = start 455 c.emit(nil) 456 enterIterBlock := c.compileForInto(into, needResult) 457 if needResult { 458 c.emit(clearResult) 459 } 460 c.compileStatement(body, needResult) 461 if enterIterBlock != nil { 462 c.leaveScopeBlock(enterIterBlock) 463 c.popScope() 464 } 465 c.emit(jump(start - len(c.p.code))) 466 if iter { 467 c.p.code[start] = iterNext(len(c.p.code) - start) 468 } else { 469 c.p.code[start] = enumNext(len(c.p.code) - start) 470 } 471 c.emit(enumPop, jump(2)) 472 c.leaveBlock() 473 c.emit(enumPopClose) 474} 475 476func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) { 477 c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, false, needResult, label) 478} 479 480func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) { 481 c.compileLabeledForOfStatement(v, needResult, "") 482} 483 484func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label unistring.String) { 485 c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, true, needResult, label) 486} 487 488func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) { 489 c.compileLabeledWhileStatement(v, needResult, "") 490} 491 492func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label unistring.String) { 493 c.block = &block{ 494 typ: blockLoop, 495 outer: c.block, 496 label: label, 497 needResult: needResult, 498 } 499 500 if needResult { 501 c.emit(clearResult) 502 } 503 start := len(c.p.code) 504 c.block.cont = start 505 expr := c.compileExpression(v.Test) 506 testTrue := false 507 var j int 508 if expr.constant() { 509 if t, ex := c.evalConst(expr); ex == nil { 510 if t.ToBoolean() { 511 testTrue = true 512 } else { 513 c.compileStatementDummy(v.Body) 514 goto end 515 } 516 } else { 517 c.emitThrow(ex.val) 518 goto end 519 } 520 } else { 521 expr.emitGetter(true) 522 j = len(c.p.code) 523 c.emit(nil) 524 } 525 if needResult { 526 c.emit(clearResult) 527 } 528 c.compileStatement(v.Body, needResult) 529 c.emit(jump(start - len(c.p.code))) 530 if !testTrue { 531 c.p.code[j] = jne(len(c.p.code) - j) 532 } 533end: 534 c.leaveBlock() 535} 536 537func (c *compiler) compileEmptyStatement(needResult bool) { 538 if needResult { 539 c.emit(clearResult) 540 } 541} 542 543func (c *compiler) compileBranchStatement(v *ast.BranchStatement) { 544 switch v.Token { 545 case token.BREAK: 546 c.compileBreak(v.Label, v.Idx) 547 case token.CONTINUE: 548 c.compileContinue(v.Label, v.Idx) 549 default: 550 panic(fmt.Errorf("Unknown branch statement token: %s", v.Token.String())) 551 } 552} 553 554func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block { 555 switch st.Token { 556 case token.BREAK: 557 return c.findBreakBlock(st.Label, true) 558 case token.CONTINUE: 559 return c.findBreakBlock(st.Label, false) 560 } 561 return nil 562} 563 564func (c *compiler) findBreakBlock(label *ast.Identifier, isBreak bool) (res *block) { 565 if label != nil { 566 var found *block 567 for b := c.block; b != nil; b = b.outer { 568 if res == nil { 569 if bb := b.breaking; bb != nil { 570 res = bb 571 if isBreak { 572 return 573 } 574 } 575 } 576 if b.label == label.Name { 577 found = b 578 break 579 } 580 } 581 if !isBreak && found != nil && found.typ != blockLoop && found.typ != blockLoopEnum { 582 c.throwSyntaxError(int(label.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", label.Name) 583 } 584 if res == nil { 585 res = found 586 } 587 } else { 588 // find the nearest loop or switch (if break) 589 L: 590 for b := c.block; b != nil; b = b.outer { 591 if bb := b.breaking; bb != nil { 592 return bb 593 } 594 switch b.typ { 595 case blockLoop, blockLoopEnum: 596 res = b 597 break L 598 case blockSwitch: 599 if isBreak { 600 res = b 601 break L 602 } 603 } 604 } 605 } 606 607 return 608} 609 610func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBreak bool) *block { 611 block := c.findBreakBlock(label, isBreak) 612 if block == nil { 613 c.throwSyntaxError(int(idx)-1, "Could not find block") 614 panic("unreachable") 615 } 616L: 617 for b := c.block; b != block; b = b.outer { 618 switch b.typ { 619 case blockIterScope: 620 if !isBreak && b.outer == block { 621 break L 622 } 623 fallthrough 624 case blockScope: 625 b.breaks = append(b.breaks, len(c.p.code)) 626 c.emit(nil) 627 case blockTry: 628 c.emit(halt) 629 case blockWith: 630 c.emit(leaveWith) 631 case blockLoopEnum: 632 c.emit(enumPopClose) 633 } 634 } 635 return block 636} 637 638func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) { 639 block := c.emitBlockExitCode(label, idx, true) 640 block.breaks = append(block.breaks, len(c.p.code)) 641 c.emit(nil) 642} 643 644func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) { 645 block := c.emitBlockExitCode(label, idx, false) 646 block.conts = append(block.conts, len(c.p.code)) 647 c.emit(nil) 648} 649 650func (c *compiler) compileIfBody(s ast.Statement, needResult bool) { 651 if !c.scope.strict { 652 if s, ok := s.(*ast.FunctionDeclaration); ok { 653 c.compileFunction(s) 654 if needResult { 655 c.emit(clearResult) 656 } 657 return 658 } 659 } 660 c.compileStatement(s, needResult) 661} 662 663func (c *compiler) compileIfBodyDummy(s ast.Statement) { 664 leave := c.enterDummyMode() 665 defer leave() 666 c.compileIfBody(s, false) 667} 668 669func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) { 670 test := c.compileExpression(v.Test) 671 if needResult { 672 c.emit(clearResult) 673 } 674 if test.constant() { 675 r, ex := c.evalConst(test) 676 if ex != nil { 677 test.addSrcMap() 678 c.emitThrow(ex.val) 679 return 680 } 681 if r.ToBoolean() { 682 c.compileIfBody(v.Consequent, needResult) 683 if v.Alternate != nil { 684 c.compileIfBodyDummy(v.Alternate) 685 } 686 } else { 687 c.compileIfBodyDummy(v.Consequent) 688 if v.Alternate != nil { 689 c.compileIfBody(v.Alternate, needResult) 690 } else { 691 if needResult { 692 c.emit(clearResult) 693 } 694 } 695 } 696 return 697 } 698 test.emitGetter(true) 699 jmp := len(c.p.code) 700 c.emit(nil) 701 c.compileIfBody(v.Consequent, needResult) 702 if v.Alternate != nil { 703 jmp1 := len(c.p.code) 704 c.emit(nil) 705 c.p.code[jmp] = jne(len(c.p.code) - jmp) 706 c.compileIfBody(v.Alternate, needResult) 707 c.p.code[jmp1] = jump(len(c.p.code) - jmp1) 708 } else { 709 if needResult { 710 c.emit(jump(2)) 711 c.p.code[jmp] = jne(len(c.p.code) - jmp) 712 c.emit(clearResult) 713 } else { 714 c.p.code[jmp] = jne(len(c.p.code) - jmp) 715 } 716 } 717} 718 719func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) { 720 if v.Argument != nil { 721 c.compileExpression(v.Argument).emitGetter(true) 722 } else { 723 c.emit(loadUndef) 724 } 725 for b := c.block; b != nil; b = b.outer { 726 switch b.typ { 727 case blockTry: 728 c.emit(halt) 729 case blockLoopEnum: 730 c.emit(enumPopClose) 731 } 732 } 733 c.emit(ret) 734} 735 736func (c *compiler) checkVarConflict(name unistring.String, offset int) { 737 for sc := c.scope; sc != nil; sc = sc.outer { 738 if b, exists := sc.boundNames[name]; exists && !b.isVar && !(b.isArg && sc != c.scope) { 739 c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name) 740 } 741 if sc.function { 742 break 743 } 744 } 745} 746 747func (c *compiler) emitVarAssign(name unistring.String, offset int, init compiledExpr) { 748 c.checkVarConflict(name, offset) 749 if init != nil { 750 c.emitVarRef(name, offset) 751 c.emitNamed(init, name) 752 c.emit(putValueP) 753 } 754} 755 756func (c *compiler) compileVarBinding(expr *ast.Binding) { 757 switch target := expr.Target.(type) { 758 case *ast.Identifier: 759 c.emitVarAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer)) 760 case ast.Pattern: 761 c.compileExpression(expr.Initializer).emitGetter(true) 762 c.emitPattern(target, c.emitPatternVarAssign, false) 763 default: 764 c.throwSyntaxError(int(target.Idx0()-1), "unsupported variable binding target: %T", target) 765 } 766} 767 768func (c *compiler) emitLexicalAssign(name unistring.String, offset int, init compiledExpr, isConst bool) { 769 b := c.scope.boundNames[name] 770 if b == nil { 771 panic("Lexical declaration for an unbound name") 772 } 773 if init != nil { 774 c.emitNamed(init, name) 775 } else { 776 if isConst { 777 c.throwSyntaxError(offset, "Missing initializer in const declaration") 778 } 779 c.emit(loadUndef) 780 } 781 if c.scope.outer != nil { 782 b.emitInit() 783 } else { 784 c.emit(initGlobal(name)) 785 } 786} 787 788func (c *compiler) emitPatternVarAssign(target, init compiledExpr) { 789 id := target.(*compiledIdentifierExpr) 790 c.emitVarAssign(id.name, id.offset, init) 791} 792 793func (c *compiler) emitPatternLexicalAssign(target, init compiledExpr, isConst bool) { 794 id := target.(*compiledIdentifierExpr) 795 c.emitLexicalAssign(id.name, id.offset, init, isConst) 796} 797 798func (c *compiler) emitPatternAssign(target, init compiledExpr) { 799 target.emitRef() 800 if id, ok := target.(*compiledIdentifierExpr); ok { 801 c.emitNamed(init, id.name) 802 } else { 803 init.emitGetter(true) 804 } 805 c.emit(putValueP) 806} 807 808func (c *compiler) compileLexicalBinding(expr *ast.Binding, isConst bool) { 809 switch target := expr.Target.(type) { 810 case *ast.Identifier: 811 c.emitLexicalAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer), isConst) 812 case ast.Pattern: 813 c.compileExpression(expr.Initializer).emitGetter(true) 814 c.emitPattern(target, func(target, init compiledExpr) { 815 c.emitPatternLexicalAssign(target, init, isConst) 816 }, false) 817 default: 818 c.throwSyntaxError(int(target.Idx0()-1), "unsupported lexical binding target: %T", target) 819 } 820} 821 822func (c *compiler) compileVariableStatement(v *ast.VariableStatement) { 823 for _, expr := range v.List { 824 c.compileVarBinding(expr) 825 } 826} 827 828func (c *compiler) compileLexicalDeclaration(v *ast.LexicalDeclaration) { 829 isConst := v.Token == token.CONST 830 for _, e := range v.List { 831 c.compileLexicalBinding(e, isConst) 832 } 833} 834 835func (c *compiler) isEmptyResult(st ast.Statement) bool { 836 switch st := st.(type) { 837 case *ast.EmptyStatement, *ast.VariableStatement, *ast.LexicalDeclaration, *ast.FunctionDeclaration, 838 *ast.BranchStatement, *ast.DebuggerStatement: 839 return true 840 case *ast.LabelledStatement: 841 return c.isEmptyResult(st.Statement) 842 case *ast.BlockStatement: 843 for _, s := range st.List { 844 if _, ok := s.(*ast.BranchStatement); ok { 845 return true 846 } 847 if !c.isEmptyResult(s) { 848 return false 849 } 850 } 851 return true 852 } 853 return false 854} 855 856func (c *compiler) scanStatements(list []ast.Statement) (lastProducingIdx int, breakingBlock *block) { 857 lastProducingIdx = -1 858 for i, st := range list { 859 if bs, ok := st.(*ast.BranchStatement); ok { 860 if blk := c.findBranchBlock(bs); blk != nil { 861 breakingBlock = blk 862 } 863 break 864 } 865 if !c.isEmptyResult(st) { 866 lastProducingIdx = i 867 } 868 } 869 return 870} 871 872func (c *compiler) compileStatementsNeedResult(list []ast.Statement, lastProducingIdx int) { 873 if lastProducingIdx >= 0 { 874 for _, st := range list[:lastProducingIdx] { 875 if _, ok := st.(*ast.FunctionDeclaration); ok { 876 continue 877 } 878 c.compileStatement(st, false) 879 } 880 c.compileStatement(list[lastProducingIdx], true) 881 } 882 var leave func() 883 defer func() { 884 if leave != nil { 885 leave() 886 } 887 }() 888 for _, st := range list[lastProducingIdx+1:] { 889 if _, ok := st.(*ast.FunctionDeclaration); ok { 890 continue 891 } 892 c.compileStatement(st, false) 893 if leave == nil { 894 if _, ok := st.(*ast.BranchStatement); ok { 895 leave = c.enterDummyMode() 896 } 897 } 898 } 899} 900 901func (c *compiler) compileStatements(list []ast.Statement, needResult bool) { 902 lastProducingIdx, blk := c.scanStatements(list) 903 if blk != nil { 904 needResult = blk.needResult 905 } 906 if needResult { 907 c.compileStatementsNeedResult(list, lastProducingIdx) 908 return 909 } 910 for _, st := range list { 911 if _, ok := st.(*ast.FunctionDeclaration); ok { 912 continue 913 } 914 c.compileStatement(st, false) 915 } 916} 917 918func (c *compiler) compileGenericLabeledStatement(v ast.Statement, needResult bool, label unistring.String) { 919 c.block = &block{ 920 typ: blockLabel, 921 outer: c.block, 922 label: label, 923 needResult: needResult, 924 } 925 c.compileStatement(v, needResult) 926 c.leaveBlock() 927} 928 929func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) { 930 var scopeDeclared bool 931 funcs := c.extractFunctions(v.List) 932 if len(funcs) > 0 { 933 c.newBlockScope() 934 scopeDeclared = true 935 } 936 c.createFunctionBindings(funcs) 937 scopeDeclared = c.compileLexicalDeclarations(v.List, scopeDeclared) 938 939 var enter *enterBlock 940 if scopeDeclared { 941 c.block = &block{ 942 outer: c.block, 943 typ: blockScope, 944 needResult: needResult, 945 } 946 enter = &enterBlock{} 947 c.emit(enter) 948 } 949 c.compileFunctions(funcs) 950 c.compileStatements(v.List, needResult) 951 if scopeDeclared { 952 c.leaveScopeBlock(enter) 953 c.popScope() 954 } 955} 956 957func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) { 958 expr := c.compileExpression(v.Expression) 959 if expr.constant() { 960 c.emitConst(expr, needResult) 961 } else { 962 expr.emitGetter(needResult) 963 } 964 if needResult { 965 c.emit(saveResult) 966 } 967} 968 969func (c *compiler) compileWithStatement(v *ast.WithStatement, needResult bool) { 970 if c.scope.strict { 971 c.throwSyntaxError(int(v.With)-1, "Strict mode code may not include a with statement") 972 return 973 } 974 c.compileExpression(v.Object).emitGetter(true) 975 c.emit(enterWith) 976 c.block = &block{ 977 outer: c.block, 978 typ: blockWith, 979 needResult: needResult, 980 } 981 c.newBlockScope() 982 c.scope.dynamic = true 983 c.compileStatement(v.Body, needResult) 984 c.emit(leaveWith) 985 c.leaveBlock() 986 c.popScope() 987} 988 989func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult bool) { 990 c.block = &block{ 991 typ: blockSwitch, 992 outer: c.block, 993 needResult: needResult, 994 } 995 996 c.compileExpression(v.Discriminant).emitGetter(true) 997 998 var funcs []*ast.FunctionDeclaration 999 for _, s := range v.Body { 1000 f := c.extractFunctions(s.Consequent) 1001 funcs = append(funcs, f...) 1002 } 1003 var scopeDeclared bool 1004 if len(funcs) > 0 { 1005 c.newBlockScope() 1006 scopeDeclared = true 1007 c.createFunctionBindings(funcs) 1008 } 1009 1010 for _, s := range v.Body { 1011 scopeDeclared = c.compileLexicalDeclarations(s.Consequent, scopeDeclared) 1012 } 1013 1014 var enter *enterBlock 1015 var db *binding 1016 if scopeDeclared { 1017 c.block = &block{ 1018 typ: blockScope, 1019 outer: c.block, 1020 needResult: needResult, 1021 } 1022 enter = &enterBlock{} 1023 c.emit(enter) 1024 // create anonymous variable for the discriminant 1025 bindings := c.scope.bindings 1026 var bb []*binding 1027 if cap(bindings) == len(bindings) { 1028 bb = make([]*binding, len(bindings)+1) 1029 } else { 1030 bb = bindings[:len(bindings)+1] 1031 } 1032 copy(bb[1:], bindings) 1033 db = &binding{ 1034 scope: c.scope, 1035 isConst: true, 1036 isStrict: true, 1037 } 1038 bb[0] = db 1039 c.scope.bindings = bb 1040 } 1041 1042 c.compileFunctions(funcs) 1043 1044 if needResult { 1045 c.emit(clearResult) 1046 } 1047 1048 jumps := make([]int, len(v.Body)) 1049 1050 for i, s := range v.Body { 1051 if s.Test != nil { 1052 if db != nil { 1053 db.emitGet() 1054 } else { 1055 c.emit(dup) 1056 } 1057 c.compileExpression(s.Test).emitGetter(true) 1058 c.emit(op_strict_eq) 1059 if db != nil { 1060 c.emit(jne(2)) 1061 } else { 1062 c.emit(jne(3), pop) 1063 } 1064 jumps[i] = len(c.p.code) 1065 c.emit(nil) 1066 } 1067 } 1068 1069 if db == nil { 1070 c.emit(pop) 1071 } 1072 jumpNoMatch := -1 1073 if v.Default != -1 { 1074 if v.Default != 0 { 1075 jumps[v.Default] = len(c.p.code) 1076 c.emit(nil) 1077 } 1078 } else { 1079 jumpNoMatch = len(c.p.code) 1080 c.emit(nil) 1081 } 1082 1083 for i, s := range v.Body { 1084 if s.Test != nil || i != 0 { 1085 c.p.code[jumps[i]] = jump(len(c.p.code) - jumps[i]) 1086 } 1087 c.compileStatements(s.Consequent, needResult) 1088 } 1089 1090 if jumpNoMatch != -1 { 1091 c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch) 1092 } 1093 if enter != nil { 1094 c.leaveScopeBlock(enter) 1095 enter.stackSize-- 1096 c.popScope() 1097 } 1098 c.leaveBlock() 1099} 1100