github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/compiler_stmt.go (about) 1 package goja 2 3 import ( 4 "github.com/nuvolaris/goja/ast" 5 "github.com/nuvolaris/goja/file" 6 "github.com/nuvolaris/goja/token" 7 "github.com/nuvolaris/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 var catchOffset int 131 if v.Catch != nil { 132 lbl2 := len(c.p.code) // jump over the catch block 133 c.emit(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) 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.p.code[lbl2] = jump(len(c.p.code) - lbl2) 186 } 187 var finallyOffset int 188 if v.Finally != nil { 189 c.emit(enterFinally{}) 190 finallyOffset = len(c.p.code) - lbl // finallyOffset should not include enterFinally 191 if bodyNeedResult && finallyBreaking != nil && lp == -1 { 192 c.emit(clearResult) 193 } 194 c.compileBlockStatement(v.Finally, false) 195 c.emit(leaveFinally{}) 196 } else { 197 c.emit(leaveTry{}) 198 } 199 c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset)} 200 c.leaveBlock() 201 } 202 203 func (c *compiler) addSrcMap(node ast.Node) { 204 c.p.addSrcMap(int(node.Idx0()) - 1) 205 } 206 207 func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) { 208 c.compileExpression(v.Argument).emitGetter(true) 209 c.addSrcMap(v) 210 c.emit(throw) 211 } 212 213 func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) { 214 c.compileLabeledDoWhileStatement(v, needResult, "") 215 } 216 217 func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label unistring.String) { 218 c.block = &block{ 219 typ: blockLoop, 220 outer: c.block, 221 label: label, 222 needResult: needResult, 223 } 224 225 start := len(c.p.code) 226 c.compileStatement(v.Body, needResult) 227 c.block.cont = len(c.p.code) 228 c.emitExpr(c.compileExpression(v.Test), true) 229 c.emit(jeq(start - len(c.p.code))) 230 c.leaveBlock() 231 } 232 233 func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) { 234 c.compileLabeledForStatement(v, needResult, "") 235 } 236 237 func (c *compiler) compileForHeadLexDecl(decl *ast.LexicalDeclaration, needResult bool) *enterBlock { 238 c.block = &block{ 239 typ: blockIterScope, 240 outer: c.block, 241 needResult: needResult, 242 } 243 244 c.newBlockScope() 245 enterIterBlock := &enterBlock{} 246 c.emit(enterIterBlock) 247 c.createLexicalBindings(decl) 248 c.compileLexicalDeclaration(decl) 249 return enterIterBlock 250 } 251 252 func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label unistring.String) { 253 loopBlock := &block{ 254 typ: blockLoop, 255 outer: c.block, 256 label: label, 257 needResult: needResult, 258 } 259 c.block = loopBlock 260 261 var enterIterBlock *enterBlock 262 switch init := v.Initializer.(type) { 263 case nil: 264 // no-op 265 case *ast.ForLoopInitializerLexicalDecl: 266 enterIterBlock = c.compileForHeadLexDecl(&init.LexicalDeclaration, needResult) 267 case *ast.ForLoopInitializerVarDeclList: 268 for _, expr := range init.List { 269 c.compileVarBinding(expr) 270 } 271 case *ast.ForLoopInitializerExpression: 272 c.compileExpression(init.Expression).emitGetter(false) 273 default: 274 c.assert(false, int(v.For)-1, "Unsupported for loop initializer: %T", init) 275 panic("unreachable") 276 } 277 278 if needResult { 279 c.emit(clearResult) // initial result 280 } 281 282 if enterIterBlock != nil { 283 c.emit(jump(1)) 284 } 285 286 start := len(c.p.code) 287 var j int 288 testConst := false 289 if v.Test != nil { 290 expr := c.compileExpression(v.Test) 291 if expr.constant() { 292 r, ex := c.evalConst(expr) 293 if ex == nil { 294 if r.ToBoolean() { 295 testConst = true 296 } else { 297 leave := c.enterDummyMode() 298 c.compileStatement(v.Body, false) 299 if v.Update != nil { 300 c.compileExpression(v.Update).emitGetter(false) 301 } 302 leave() 303 goto end 304 } 305 } else { 306 expr.addSrcMap() 307 c.emitThrow(ex.val) 308 goto end 309 } 310 } else { 311 expr.emitGetter(true) 312 j = len(c.p.code) 313 c.emit(nil) 314 } 315 } 316 if needResult { 317 c.emit(clearResult) 318 } 319 c.compileStatement(v.Body, needResult) 320 loopBlock.cont = len(c.p.code) 321 if enterIterBlock != nil { 322 c.emit(jump(1)) 323 } 324 if v.Update != nil { 325 c.compileExpression(v.Update).emitGetter(false) 326 } 327 if enterIterBlock != nil { 328 if c.scope.needStash || c.scope.isDynamic() { 329 c.p.code[start-1] = copyStash{} 330 c.p.code[loopBlock.cont] = copyStash{} 331 } else { 332 if l := len(c.p.code); l > loopBlock.cont { 333 loopBlock.cont++ 334 } else { 335 c.p.code = c.p.code[:l-1] 336 } 337 } 338 } 339 c.emit(jump(start - len(c.p.code))) 340 if v.Test != nil { 341 if !testConst { 342 c.p.code[j] = jne(len(c.p.code) - j) 343 } 344 } 345 end: 346 if enterIterBlock != nil { 347 c.leaveScopeBlock(enterIterBlock) 348 c.popScope() 349 } 350 c.leaveBlock() 351 } 352 353 func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) { 354 c.compileLabeledForInStatement(v, needResult, "") 355 } 356 357 func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *enterBlock) { 358 switch into := into.(type) { 359 case *ast.ForIntoExpression: 360 c.compileExpression(into.Expression).emitSetter(&c.enumGetExpr, false) 361 case *ast.ForIntoVar: 362 if c.scope.strict && into.Binding.Initializer != nil { 363 c.throwSyntaxError(int(into.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.") 364 } 365 switch target := into.Binding.Target.(type) { 366 case *ast.Identifier: 367 c.compileIdentifierExpression(target).emitSetter(&c.enumGetExpr, false) 368 case ast.Pattern: 369 c.emit(enumGet) 370 c.emitPattern(target, c.emitPatternVarAssign, false) 371 default: 372 c.throwSyntaxError(int(target.Idx0()-1), "unsupported for-in var target: %T", target) 373 } 374 case *ast.ForDeclaration: 375 376 c.block = &block{ 377 typ: blockIterScope, 378 outer: c.block, 379 needResult: needResult, 380 } 381 382 c.newBlockScope() 383 enter = &enterBlock{} 384 c.emit(enter) 385 switch target := into.Target.(type) { 386 case *ast.Identifier: 387 b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1) 388 c.emit(enumGet) 389 b.emitInitP() 390 case ast.Pattern: 391 c.createLexicalBinding(target, into.IsConst) 392 c.emit(enumGet) 393 c.emitPattern(target, func(target, init compiledExpr) { 394 c.emitPatternLexicalAssign(target, init) 395 }, false) 396 default: 397 c.assert(false, int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target) 398 } 399 default: 400 c.assert(false, int(into.Idx0())-1, "Unsupported for-into: %T", into) 401 panic("unreachable") 402 } 403 404 return 405 } 406 407 func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.Expression, body ast.Statement, iter, needResult bool, label unistring.String) { 408 c.block = &block{ 409 typ: blockLoopEnum, 410 outer: c.block, 411 label: label, 412 needResult: needResult, 413 } 414 enterPos := -1 415 if forDecl, ok := into.(*ast.ForDeclaration); ok { 416 c.block = &block{ 417 typ: blockScope, 418 outer: c.block, 419 needResult: false, 420 } 421 c.newBlockScope() 422 enterPos = len(c.p.code) 423 c.emit(jump(1)) 424 c.createLexicalBinding(forDecl.Target, forDecl.IsConst) 425 } 426 c.compileExpression(source).emitGetter(true) 427 if enterPos != -1 { 428 s := c.scope 429 used := len(c.block.breaks) > 0 || s.isDynamic() 430 if !used { 431 for _, b := range s.bindings { 432 if b.useCount() > 0 { 433 used = true 434 break 435 } 436 } 437 } 438 if used { 439 // We need the stack untouched because it contains the source. 440 // This is not the most optimal way, but it's an edge case, hopefully quite rare. 441 for _, b := range s.bindings { 442 b.moveToStash() 443 } 444 enter := &enterBlock{} 445 c.p.code[enterPos] = enter 446 c.leaveScopeBlock(enter) 447 } else { 448 c.block = c.block.outer 449 } 450 c.popScope() 451 } 452 if iter { 453 c.emit(iterateP) 454 } else { 455 c.emit(enumerate) 456 } 457 if needResult { 458 c.emit(clearResult) 459 } 460 start := len(c.p.code) 461 c.block.cont = start 462 c.emit(nil) 463 enterIterBlock := c.compileForInto(into, needResult) 464 if needResult { 465 c.emit(clearResult) 466 } 467 c.compileStatement(body, needResult) 468 if enterIterBlock != nil { 469 c.leaveScopeBlock(enterIterBlock) 470 c.popScope() 471 } 472 c.emit(jump(start - len(c.p.code))) 473 if iter { 474 c.p.code[start] = iterNext(len(c.p.code) - start) 475 } else { 476 c.p.code[start] = enumNext(len(c.p.code) - start) 477 } 478 c.emit(enumPop, jump(2)) 479 c.leaveBlock() 480 c.emit(enumPopClose) 481 } 482 483 func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) { 484 c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, false, needResult, label) 485 } 486 487 func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) { 488 c.compileLabeledForOfStatement(v, needResult, "") 489 } 490 491 func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label unistring.String) { 492 c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, true, needResult, label) 493 } 494 495 func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) { 496 c.compileLabeledWhileStatement(v, needResult, "") 497 } 498 499 func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label unistring.String) { 500 c.block = &block{ 501 typ: blockLoop, 502 outer: c.block, 503 label: label, 504 needResult: needResult, 505 } 506 507 if needResult { 508 c.emit(clearResult) 509 } 510 start := len(c.p.code) 511 c.block.cont = start 512 expr := c.compileExpression(v.Test) 513 testTrue := false 514 var j int 515 if expr.constant() { 516 if t, ex := c.evalConst(expr); ex == nil { 517 if t.ToBoolean() { 518 testTrue = true 519 } else { 520 c.compileStatementDummy(v.Body) 521 goto end 522 } 523 } else { 524 c.emitThrow(ex.val) 525 goto end 526 } 527 } else { 528 expr.emitGetter(true) 529 j = len(c.p.code) 530 c.emit(nil) 531 } 532 if needResult { 533 c.emit(clearResult) 534 } 535 c.compileStatement(v.Body, needResult) 536 c.emit(jump(start - len(c.p.code))) 537 if !testTrue { 538 c.p.code[j] = jne(len(c.p.code) - j) 539 } 540 end: 541 c.leaveBlock() 542 } 543 544 func (c *compiler) compileEmptyStatement(needResult bool) { 545 if needResult { 546 c.emit(clearResult) 547 } 548 } 549 550 func (c *compiler) compileBranchStatement(v *ast.BranchStatement) { 551 switch v.Token { 552 case token.BREAK: 553 c.compileBreak(v.Label, v.Idx) 554 case token.CONTINUE: 555 c.compileContinue(v.Label, v.Idx) 556 default: 557 c.assert(false, int(v.Idx0())-1, "Unknown branch statement token: %s", v.Token.String()) 558 panic("unreachable") 559 } 560 } 561 562 func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block { 563 switch st.Token { 564 case token.BREAK: 565 return c.findBreakBlock(st.Label, true) 566 case token.CONTINUE: 567 return c.findBreakBlock(st.Label, false) 568 } 569 return nil 570 } 571 572 func (c *compiler) findBreakBlock(label *ast.Identifier, isBreak bool) (res *block) { 573 if label != nil { 574 var found *block 575 for b := c.block; b != nil; b = b.outer { 576 if res == nil { 577 if bb := b.breaking; bb != nil { 578 res = bb 579 if isBreak { 580 return 581 } 582 } 583 } 584 if b.label == label.Name { 585 found = b 586 break 587 } 588 } 589 if !isBreak && found != nil && found.typ != blockLoop && found.typ != blockLoopEnum { 590 c.throwSyntaxError(int(label.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", label.Name) 591 } 592 if res == nil { 593 res = found 594 } 595 } else { 596 // find the nearest loop or switch (if break) 597 L: 598 for b := c.block; b != nil; b = b.outer { 599 if bb := b.breaking; bb != nil { 600 return bb 601 } 602 switch b.typ { 603 case blockLoop, blockLoopEnum: 604 res = b 605 break L 606 case blockSwitch: 607 if isBreak { 608 res = b 609 break L 610 } 611 } 612 } 613 } 614 615 return 616 } 617 618 func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBreak bool) *block { 619 block := c.findBreakBlock(label, isBreak) 620 if block == nil { 621 c.throwSyntaxError(int(idx)-1, "Could not find block") 622 panic("unreachable") 623 } 624 contForLoop := !isBreak && block.typ == blockLoop 625 L: 626 for b := c.block; b != block; b = b.outer { 627 switch b.typ { 628 case blockIterScope: 629 // blockIterScope in 'for' loops is shared across iterations, so 630 // continue should not pop it. 631 if contForLoop && 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(leaveTry{}) 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 && !s.Function.Async && !s.Function.Generator { 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(leaveTry{}) 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 }