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