github.com/dop251/goja@v0.0.0-20240220182346-e401ed450204/compiler.go (about) 1 package goja 2 3 import ( 4 "fmt" 5 "github.com/dop251/goja/token" 6 "sort" 7 8 "github.com/dop251/goja/ast" 9 "github.com/dop251/goja/file" 10 "github.com/dop251/goja/unistring" 11 ) 12 13 type blockType int 14 15 const ( 16 blockLoop blockType = iota 17 blockLoopEnum 18 blockTry 19 blockLabel 20 blockSwitch 21 blockWith 22 blockScope 23 blockIterScope 24 blockOptChain 25 ) 26 27 const ( 28 maskConst = 1 << 31 29 maskVar = 1 << 30 30 maskDeletable = 1 << 29 31 maskStrict = maskDeletable 32 33 maskTyp = maskConst | maskVar | maskDeletable 34 ) 35 36 type varType byte 37 38 const ( 39 varTypeVar varType = iota 40 varTypeLet 41 varTypeStrictConst 42 varTypeConst 43 ) 44 45 const thisBindingName = " this" // must not be a valid identifier 46 47 type CompilerError struct { 48 Message string 49 File *file.File 50 Offset int 51 } 52 53 type CompilerSyntaxError struct { 54 CompilerError 55 } 56 57 type CompilerReferenceError struct { 58 CompilerError 59 } 60 61 type srcMapItem struct { 62 pc int 63 srcPos int 64 } 65 66 // Program is an internal, compiled representation of code which is produced by the Compile function. 67 // This representation is not linked to a runtime in any way and can be used concurrently. 68 // It is always preferable to use a Program over a string when running code as it skips the compilation step. 69 type Program struct { 70 code []instruction 71 values []Value 72 73 funcName unistring.String 74 src *file.File 75 srcMap []srcMapItem 76 } 77 78 type compiler struct { 79 p *Program 80 scope *scope 81 block *block 82 83 classScope *classScope 84 85 enumGetExpr compiledEnumGetExpr 86 87 evalVM *vm // VM used to evaluate constant expressions 88 ctxVM *vm // VM in which an eval() code is compiled 89 90 codeScratchpad []instruction 91 } 92 93 type binding struct { 94 scope *scope 95 name unistring.String 96 accessPoints map[*scope]*[]int 97 isConst bool 98 isStrict bool 99 isArg bool 100 isVar bool 101 inStash bool 102 } 103 104 func (b *binding) getAccessPointsForScope(s *scope) *[]int { 105 m := b.accessPoints[s] 106 if m == nil { 107 a := make([]int, 0, 1) 108 m = &a 109 if b.accessPoints == nil { 110 b.accessPoints = make(map[*scope]*[]int) 111 } 112 b.accessPoints[s] = m 113 } 114 return m 115 } 116 117 func (b *binding) markAccessPointAt(pos int) { 118 scope := b.scope.c.scope 119 m := b.getAccessPointsForScope(scope) 120 *m = append(*m, pos-scope.base) 121 } 122 123 func (b *binding) markAccessPointAtScope(scope *scope, pos int) { 124 m := b.getAccessPointsForScope(scope) 125 *m = append(*m, pos-scope.base) 126 } 127 128 func (b *binding) markAccessPoint() { 129 scope := b.scope.c.scope 130 m := b.getAccessPointsForScope(scope) 131 *m = append(*m, len(scope.prg.code)-scope.base) 132 } 133 134 func (b *binding) emitGet() { 135 b.markAccessPoint() 136 if b.isVar && !b.isArg { 137 b.scope.c.emit(loadStack(0)) 138 } else { 139 b.scope.c.emit(loadStackLex(0)) 140 } 141 } 142 143 func (b *binding) emitGetAt(pos int) { 144 b.markAccessPointAt(pos) 145 if b.isVar && !b.isArg { 146 b.scope.c.p.code[pos] = loadStack(0) 147 } else { 148 b.scope.c.p.code[pos] = loadStackLex(0) 149 } 150 } 151 152 func (b *binding) emitGetP() { 153 if b.isVar && !b.isArg { 154 // no-op 155 } else { 156 // make sure TDZ is checked 157 b.markAccessPoint() 158 b.scope.c.emit(loadStackLex(0), pop) 159 } 160 } 161 162 func (b *binding) emitSet() { 163 if b.isConst { 164 if b.isStrict || b.scope.c.scope.strict { 165 b.scope.c.emit(throwAssignToConst) 166 } 167 return 168 } 169 b.markAccessPoint() 170 if b.isVar && !b.isArg { 171 b.scope.c.emit(storeStack(0)) 172 } else { 173 b.scope.c.emit(storeStackLex(0)) 174 } 175 } 176 177 func (b *binding) emitSetP() { 178 if b.isConst { 179 if b.isStrict || b.scope.c.scope.strict { 180 b.scope.c.emit(throwAssignToConst) 181 } 182 return 183 } 184 b.markAccessPoint() 185 if b.isVar && !b.isArg { 186 b.scope.c.emit(storeStackP(0)) 187 } else { 188 b.scope.c.emit(storeStackLexP(0)) 189 } 190 } 191 192 func (b *binding) emitInitP() { 193 if !b.isVar && b.scope.outer == nil { 194 b.scope.c.emit(initGlobalP(b.name)) 195 } else { 196 b.markAccessPoint() 197 b.scope.c.emit(initStackP(0)) 198 } 199 } 200 201 func (b *binding) emitInit() { 202 if !b.isVar && b.scope.outer == nil { 203 b.scope.c.emit(initGlobal(b.name)) 204 } else { 205 b.markAccessPoint() 206 b.scope.c.emit(initStack(0)) 207 } 208 } 209 210 func (b *binding) emitInitAt(pos int) { 211 if !b.isVar && b.scope.outer == nil { 212 b.scope.c.p.code[pos] = initGlobal(b.name) 213 } else { 214 b.markAccessPointAt(pos) 215 b.scope.c.p.code[pos] = initStack(0) 216 } 217 } 218 219 func (b *binding) emitInitAtScope(scope *scope, pos int) { 220 if !b.isVar && scope.outer == nil { 221 scope.c.p.code[pos] = initGlobal(b.name) 222 } else { 223 b.markAccessPointAtScope(scope, pos) 224 scope.c.p.code[pos] = initStack(0) 225 } 226 } 227 228 func (b *binding) emitInitPAtScope(scope *scope, pos int) { 229 if !b.isVar && scope.outer == nil { 230 scope.c.p.code[pos] = initGlobalP(b.name) 231 } else { 232 b.markAccessPointAtScope(scope, pos) 233 scope.c.p.code[pos] = initStackP(0) 234 } 235 } 236 237 func (b *binding) emitGetVar(callee bool) { 238 b.markAccessPoint() 239 if b.isVar && !b.isArg { 240 b.scope.c.emit(&loadMixed{name: b.name, callee: callee}) 241 } else { 242 b.scope.c.emit(&loadMixedLex{name: b.name, callee: callee}) 243 } 244 } 245 246 func (b *binding) emitResolveVar(strict bool) { 247 b.markAccessPoint() 248 if b.isVar && !b.isArg { 249 b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: varTypeVar}) 250 } else { 251 var typ varType 252 if b.isConst { 253 if b.isStrict { 254 typ = varTypeStrictConst 255 } else { 256 typ = varTypeConst 257 } 258 } else { 259 typ = varTypeLet 260 } 261 b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: typ}) 262 } 263 } 264 265 func (b *binding) moveToStash() { 266 if b.isArg && !b.scope.argsInStash { 267 b.scope.moveArgsToStash() 268 } else { 269 b.inStash = true 270 b.scope.needStash = true 271 } 272 } 273 274 func (b *binding) useCount() (count int) { 275 for _, a := range b.accessPoints { 276 count += len(*a) 277 } 278 return 279 } 280 281 type scope struct { 282 c *compiler 283 prg *Program 284 outer *scope 285 nested []*scope 286 boundNames map[unistring.String]*binding 287 bindings []*binding 288 base int 289 numArgs int 290 291 // function type. If not funcNone, this is a function or a top-level lexical environment 292 funcType funcType 293 294 // in strict mode 295 strict bool 296 // eval top-level scope 297 eval bool 298 // at least one inner scope has direct eval() which can lookup names dynamically (by name) 299 dynLookup bool 300 // at least one binding has been marked for placement in stash 301 needStash bool 302 303 // is a variable environment, i.e. the target for dynamically created var bindings 304 variable bool 305 // a function scope that has at least one direct eval() and non-strict, so the variables can be added dynamically 306 dynamic bool 307 // arguments have been marked for placement in stash (functions only) 308 argsInStash bool 309 // need 'arguments' object (functions only) 310 argsNeeded bool 311 } 312 313 type block struct { 314 typ blockType 315 label unistring.String 316 cont int 317 breaks []int 318 conts []int 319 outer *block 320 breaking *block // set when the 'finally' block is an empty break statement sequence 321 needResult bool 322 } 323 324 func (c *compiler) leaveScopeBlock(enter *enterBlock) { 325 c.updateEnterBlock(enter) 326 leave := &leaveBlock{ 327 stackSize: enter.stackSize, 328 popStash: enter.stashSize > 0, 329 } 330 c.emit(leave) 331 for _, pc := range c.block.breaks { 332 c.p.code[pc] = leave 333 } 334 c.block.breaks = nil 335 c.leaveBlock() 336 } 337 338 func (c *compiler) leaveBlock() { 339 lbl := len(c.p.code) 340 for _, item := range c.block.breaks { 341 c.p.code[item] = jump(lbl - item) 342 } 343 if t := c.block.typ; t == blockLoop || t == blockLoopEnum { 344 for _, item := range c.block.conts { 345 c.p.code[item] = jump(c.block.cont - item) 346 } 347 } 348 c.block = c.block.outer 349 } 350 351 func (e *CompilerSyntaxError) Error() string { 352 if e.File != nil { 353 return fmt.Sprintf("SyntaxError: %s at %s", e.Message, e.File.Position(e.Offset)) 354 } 355 return fmt.Sprintf("SyntaxError: %s", e.Message) 356 } 357 358 func (e *CompilerReferenceError) Error() string { 359 return fmt.Sprintf("ReferenceError: %s", e.Message) 360 } 361 362 func (c *compiler) newScope() { 363 strict := false 364 if c.scope != nil { 365 strict = c.scope.strict 366 } 367 c.scope = &scope{ 368 c: c, 369 prg: c.p, 370 outer: c.scope, 371 strict: strict, 372 } 373 } 374 375 func (c *compiler) newBlockScope() { 376 c.newScope() 377 if outer := c.scope.outer; outer != nil { 378 outer.nested = append(outer.nested, c.scope) 379 } 380 c.scope.base = len(c.p.code) 381 } 382 383 func (c *compiler) popScope() { 384 c.scope = c.scope.outer 385 } 386 387 func newCompiler() *compiler { 388 c := &compiler{ 389 p: &Program{}, 390 } 391 392 c.enumGetExpr.init(c, file.Idx(0)) 393 394 return c 395 } 396 397 func (p *Program) defineLiteralValue(val Value) uint32 { 398 for idx, v := range p.values { 399 if v.SameAs(val) { 400 return uint32(idx) 401 } 402 } 403 idx := uint32(len(p.values)) 404 p.values = append(p.values, val) 405 return idx 406 } 407 408 func (p *Program) dumpCode(logger func(format string, args ...interface{})) { 409 p._dumpCode("", logger) 410 } 411 412 func (p *Program) _dumpCode(indent string, logger func(format string, args ...interface{})) { 413 logger("values: %+v", p.values) 414 dumpInitFields := func(initFields *Program) { 415 i := indent + ">" 416 logger("%s ---- init_fields:", i) 417 initFields._dumpCode(i, logger) 418 logger("%s ----", i) 419 } 420 for pc, ins := range p.code { 421 logger("%s %d: %T(%v)", indent, pc, ins, ins) 422 var prg *Program 423 switch f := ins.(type) { 424 case newFuncInstruction: 425 prg = f.getPrg() 426 case *newDerivedClass: 427 if f.initFields != nil { 428 dumpInitFields(f.initFields) 429 } 430 prg = f.ctor 431 case *newClass: 432 if f.initFields != nil { 433 dumpInitFields(f.initFields) 434 } 435 prg = f.ctor 436 case *newStaticFieldInit: 437 if f.initFields != nil { 438 dumpInitFields(f.initFields) 439 } 440 } 441 if prg != nil { 442 prg._dumpCode(indent+">", logger) 443 } 444 } 445 } 446 447 func (p *Program) sourceOffset(pc int) int { 448 i := sort.Search(len(p.srcMap), func(idx int) bool { 449 return p.srcMap[idx].pc > pc 450 }) - 1 451 if i >= 0 { 452 return p.srcMap[i].srcPos 453 } 454 455 return 0 456 } 457 458 func (p *Program) addSrcMap(srcPos int) { 459 if len(p.srcMap) > 0 && p.srcMap[len(p.srcMap)-1].srcPos == srcPos { 460 return 461 } 462 p.srcMap = append(p.srcMap, srcMapItem{pc: len(p.code), srcPos: srcPos}) 463 } 464 465 func (s *scope) lookupName(name unistring.String) (binding *binding, noDynamics bool) { 466 noDynamics = true 467 toStash := false 468 for curScope := s; ; curScope = curScope.outer { 469 if curScope.outer != nil { 470 if b, exists := curScope.boundNames[name]; exists { 471 if toStash && !b.inStash { 472 b.moveToStash() 473 } 474 binding = b 475 return 476 } 477 } else { 478 noDynamics = false 479 return 480 } 481 if curScope.dynamic { 482 noDynamics = false 483 } 484 if name == "arguments" && curScope.funcType != funcNone && curScope.funcType != funcArrow { 485 if curScope.funcType == funcClsInit { 486 s.c.throwSyntaxError(0, "'arguments' is not allowed in class field initializer or static initialization block") 487 } 488 curScope.argsNeeded = true 489 binding, _ = curScope.bindName(name) 490 return 491 } 492 if curScope.isFunction() { 493 toStash = true 494 } 495 } 496 } 497 498 func (s *scope) lookupThis() (*binding, bool) { 499 toStash := false 500 for curScope := s; curScope != nil; curScope = curScope.outer { 501 if curScope.outer == nil { 502 if curScope.eval { 503 return nil, true 504 } 505 } 506 if b, exists := curScope.boundNames[thisBindingName]; exists { 507 if toStash && !b.inStash { 508 b.moveToStash() 509 } 510 return b, false 511 } 512 if curScope.isFunction() { 513 toStash = true 514 } 515 } 516 return nil, false 517 } 518 519 func (s *scope) ensureBoundNamesCreated() { 520 if s.boundNames == nil { 521 s.boundNames = make(map[unistring.String]*binding) 522 } 523 } 524 525 func (s *scope) addBinding(offset int) *binding { 526 if len(s.bindings) >= (1<<24)-1 { 527 s.c.throwSyntaxError(offset, "Too many variables") 528 } 529 b := &binding{ 530 scope: s, 531 } 532 s.bindings = append(s.bindings, b) 533 return b 534 } 535 536 func (s *scope) bindNameLexical(name unistring.String, unique bool, offset int) (*binding, bool) { 537 if b := s.boundNames[name]; b != nil { 538 if unique { 539 s.c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name) 540 } 541 return b, false 542 } 543 b := s.addBinding(offset) 544 b.name = name 545 s.ensureBoundNamesCreated() 546 s.boundNames[name] = b 547 return b, true 548 } 549 550 func (s *scope) createThisBinding() *binding { 551 thisBinding, _ := s.bindNameLexical(thisBindingName, false, 0) 552 thisBinding.isVar = true // don't check on load 553 return thisBinding 554 } 555 556 func (s *scope) bindName(name unistring.String) (*binding, bool) { 557 if !s.isFunction() && !s.variable && s.outer != nil { 558 return s.outer.bindName(name) 559 } 560 b, created := s.bindNameLexical(name, false, 0) 561 if created { 562 b.isVar = true 563 } 564 return b, created 565 } 566 567 func (s *scope) bindNameShadow(name unistring.String) (*binding, bool) { 568 if !s.isFunction() && s.outer != nil { 569 return s.outer.bindNameShadow(name) 570 } 571 572 _, exists := s.boundNames[name] 573 b := &binding{ 574 scope: s, 575 name: name, 576 } 577 s.bindings = append(s.bindings, b) 578 s.ensureBoundNamesCreated() 579 s.boundNames[name] = b 580 return b, !exists 581 } 582 583 func (s *scope) nearestFunction() *scope { 584 for sc := s; sc != nil; sc = sc.outer { 585 if sc.isFunction() { 586 return sc 587 } 588 } 589 return nil 590 } 591 592 func (s *scope) nearestThis() *scope { 593 for sc := s; sc != nil; sc = sc.outer { 594 if sc.eval || sc.isFunction() && sc.funcType != funcArrow { 595 return sc 596 } 597 } 598 return nil 599 } 600 601 func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) { 602 argsInStash := false 603 if f := s.nearestFunction(); f != nil { 604 argsInStash = f.argsInStash 605 } 606 stackIdx, stashIdx := 0, 0 607 allInStash := s.isDynamic() 608 var derivedCtor bool 609 if fs := s.nearestThis(); fs != nil && fs.funcType == funcDerivedCtor { 610 derivedCtor = true 611 } 612 for i, b := range s.bindings { 613 var this bool 614 if b.name == thisBindingName { 615 this = true 616 } 617 if allInStash || b.inStash { 618 for scope, aps := range b.accessPoints { 619 var level uint32 620 for sc := scope; sc != nil && sc != s; sc = sc.outer { 621 if sc.needStash || sc.isDynamic() { 622 level++ 623 } 624 } 625 if level > 255 { 626 s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded") 627 } 628 idx := (level << 24) | uint32(stashIdx) 629 base := scope.base 630 code := scope.prg.code 631 if this { 632 if derivedCtor { 633 for _, pc := range *aps { 634 ap := &code[base+pc] 635 switch (*ap).(type) { 636 case loadStack: 637 *ap = loadThisStash(idx) 638 case initStack: 639 *ap = initStash(idx) 640 case resolveThisStack: 641 *ap = resolveThisStash(idx) 642 case _ret: 643 *ap = cret(idx) 644 default: 645 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'") 646 } 647 } 648 } else { 649 for _, pc := range *aps { 650 ap := &code[base+pc] 651 switch (*ap).(type) { 652 case loadStack: 653 *ap = loadStash(idx) 654 case initStack: 655 *ap = initStash(idx) 656 default: 657 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'") 658 } 659 } 660 } 661 } else { 662 for _, pc := range *aps { 663 ap := &code[base+pc] 664 switch i := (*ap).(type) { 665 case loadStack: 666 *ap = loadStash(idx) 667 case storeStack: 668 *ap = storeStash(idx) 669 case storeStackP: 670 *ap = storeStashP(idx) 671 case loadStackLex: 672 *ap = loadStashLex(idx) 673 case storeStackLex: 674 *ap = storeStashLex(idx) 675 case storeStackLexP: 676 *ap = storeStashLexP(idx) 677 case initStackP: 678 *ap = initStashP(idx) 679 case initStack: 680 *ap = initStash(idx) 681 case *loadMixed: 682 i.idx = idx 683 case *loadMixedLex: 684 i.idx = idx 685 case *resolveMixed: 686 i.idx = idx 687 default: 688 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i) 689 } 690 } 691 } 692 } 693 stashIdx++ 694 } else { 695 var idx int 696 if !this { 697 if i < s.numArgs { 698 idx = -(i + 1) 699 } else { 700 stackIdx++ 701 idx = stackIdx + stackOffset 702 } 703 } 704 for scope, aps := range b.accessPoints { 705 var level int 706 for sc := scope; sc != nil && sc != s; sc = sc.outer { 707 if sc.needStash || sc.isDynamic() { 708 level++ 709 } 710 } 711 if level > 255 { 712 s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded") 713 } 714 code := scope.prg.code 715 base := scope.base 716 if this { 717 if derivedCtor { 718 for _, pc := range *aps { 719 ap := &code[base+pc] 720 switch (*ap).(type) { 721 case loadStack: 722 *ap = loadThisStack{} 723 case initStack: 724 // no-op 725 case resolveThisStack: 726 // no-op 727 case _ret: 728 // no-op, already in the right place 729 default: 730 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'") 731 } 732 } 733 } /*else { 734 no-op 735 }*/ 736 } else if argsInStash { 737 for _, pc := range *aps { 738 ap := &code[base+pc] 739 switch i := (*ap).(type) { 740 case loadStack: 741 *ap = loadStack1(idx) 742 case storeStack: 743 *ap = storeStack1(idx) 744 case storeStackP: 745 *ap = storeStack1P(idx) 746 case loadStackLex: 747 *ap = loadStack1Lex(idx) 748 case storeStackLex: 749 *ap = storeStack1Lex(idx) 750 case storeStackLexP: 751 *ap = storeStack1LexP(idx) 752 case initStackP: 753 *ap = initStack1P(idx) 754 case initStack: 755 *ap = initStack1(idx) 756 case *loadMixed: 757 *ap = &loadMixedStack1{name: i.name, idx: idx, level: uint8(level), callee: i.callee} 758 case *loadMixedLex: 759 *ap = &loadMixedStack1Lex{name: i.name, idx: idx, level: uint8(level), callee: i.callee} 760 case *resolveMixed: 761 *ap = &resolveMixedStack1{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict} 762 default: 763 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i) 764 } 765 } 766 } else { 767 for _, pc := range *aps { 768 ap := &code[base+pc] 769 switch i := (*ap).(type) { 770 case loadStack: 771 *ap = loadStack(idx) 772 case storeStack: 773 *ap = storeStack(idx) 774 case storeStackP: 775 *ap = storeStackP(idx) 776 case loadStackLex: 777 *ap = loadStackLex(idx) 778 case storeStackLex: 779 *ap = storeStackLex(idx) 780 case storeStackLexP: 781 *ap = storeStackLexP(idx) 782 case initStack: 783 *ap = initStack(idx) 784 case initStackP: 785 *ap = initStackP(idx) 786 case *loadMixed: 787 *ap = &loadMixedStack{name: i.name, idx: idx, level: uint8(level), callee: i.callee} 788 case *loadMixedLex: 789 *ap = &loadMixedStackLex{name: i.name, idx: idx, level: uint8(level), callee: i.callee} 790 case *resolveMixed: 791 *ap = &resolveMixedStack{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict} 792 default: 793 s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i) 794 } 795 } 796 } 797 } 798 } 799 } 800 for _, nested := range s.nested { 801 nested.finaliseVarAlloc(stackIdx + stackOffset) 802 } 803 return stashIdx, stackIdx 804 } 805 806 func (s *scope) moveArgsToStash() { 807 for _, b := range s.bindings { 808 if !b.isArg { 809 break 810 } 811 b.inStash = true 812 } 813 s.argsInStash = true 814 s.needStash = true 815 } 816 817 func (c *compiler) trimCode(delta int) { 818 src := c.p.code[delta:] 819 newCode := make([]instruction, len(src)) 820 copy(newCode, src) 821 if cap(c.codeScratchpad) < cap(c.p.code) { 822 c.codeScratchpad = c.p.code[:0] 823 } 824 c.p.code = newCode 825 } 826 827 func (s *scope) trimCode(delta int) { 828 s.c.trimCode(delta) 829 if delta != 0 { 830 srcMap := s.c.p.srcMap 831 for i := range srcMap { 832 srcMap[i].pc -= delta 833 } 834 s.adjustBase(-delta) 835 } 836 } 837 838 func (s *scope) adjustBase(delta int) { 839 s.base += delta 840 for _, nested := range s.nested { 841 nested.adjustBase(delta) 842 } 843 } 844 845 func (s *scope) makeNamesMap() map[unistring.String]uint32 { 846 l := len(s.bindings) 847 if l == 0 { 848 return nil 849 } 850 names := make(map[unistring.String]uint32, l) 851 for i, b := range s.bindings { 852 idx := uint32(i) 853 if b.isConst { 854 idx |= maskConst 855 if b.isStrict { 856 idx |= maskStrict 857 } 858 } 859 if b.isVar { 860 idx |= maskVar 861 } 862 names[b.name] = idx 863 } 864 return names 865 } 866 867 func (s *scope) isDynamic() bool { 868 return s.dynLookup || s.dynamic 869 } 870 871 func (s *scope) isFunction() bool { 872 return s.funcType != funcNone && !s.eval 873 } 874 875 func (s *scope) deleteBinding(b *binding) { 876 idx := 0 877 for i, bb := range s.bindings { 878 if bb == b { 879 idx = i 880 goto found 881 } 882 } 883 return 884 found: 885 delete(s.boundNames, b.name) 886 copy(s.bindings[idx:], s.bindings[idx+1:]) 887 l := len(s.bindings) - 1 888 s.bindings[l] = nil 889 s.bindings = s.bindings[:l] 890 } 891 892 func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) { 893 c.ctxVM = evalVm 894 895 eval := evalVm != nil 896 c.p.src = in.File 897 c.newScope() 898 scope := c.scope 899 scope.dynamic = true 900 scope.eval = eval 901 if !strict && len(in.Body) > 0 { 902 strict = c.isStrict(in.Body) != nil 903 } 904 scope.strict = strict 905 ownVarScope := eval && strict 906 ownLexScope := !inGlobal || eval 907 if ownVarScope { 908 c.newBlockScope() 909 scope = c.scope 910 scope.variable = true 911 } 912 if eval && !inGlobal { 913 for s := evalVm.stash; s != nil; s = s.outer { 914 if ft := s.funcType; ft != funcNone && ft != funcArrow { 915 scope.funcType = ft 916 break 917 } 918 } 919 } 920 funcs := c.extractFunctions(in.Body) 921 c.createFunctionBindings(funcs) 922 numFuncs := len(scope.bindings) 923 if inGlobal && !ownVarScope { 924 if numFuncs == len(funcs) { 925 c.compileFunctionsGlobalAllUnique(funcs) 926 } else { 927 c.compileFunctionsGlobal(funcs) 928 } 929 } 930 c.compileDeclList(in.DeclarationList, false) 931 numVars := len(scope.bindings) - numFuncs 932 vars := make([]unistring.String, len(scope.bindings)) 933 for i, b := range scope.bindings { 934 vars[i] = b.name 935 } 936 if len(vars) > 0 && !ownVarScope && ownLexScope { 937 if inGlobal { 938 c.emit(&bindGlobal{ 939 vars: vars[numFuncs:], 940 funcs: vars[:numFuncs], 941 deletable: eval, 942 }) 943 } else { 944 c.emit(&bindVars{names: vars, deletable: eval}) 945 } 946 } 947 var enter *enterBlock 948 if c.compileLexicalDeclarations(in.Body, ownVarScope || !ownLexScope) { 949 if ownLexScope { 950 c.block = &block{ 951 outer: c.block, 952 typ: blockScope, 953 needResult: true, 954 } 955 enter = &enterBlock{} 956 c.emit(enter) 957 } 958 } 959 if len(scope.bindings) > 0 && !ownLexScope { 960 var lets, consts []unistring.String 961 for _, b := range c.scope.bindings[numFuncs+numVars:] { 962 if b.isConst { 963 consts = append(consts, b.name) 964 } else { 965 lets = append(lets, b.name) 966 } 967 } 968 c.emit(&bindGlobal{ 969 vars: vars[numFuncs:], 970 funcs: vars[:numFuncs], 971 lets: lets, 972 consts: consts, 973 }) 974 } 975 if !inGlobal || ownVarScope { 976 c.compileFunctions(funcs) 977 } 978 c.compileStatements(in.Body, true) 979 if enter != nil { 980 c.leaveScopeBlock(enter) 981 c.popScope() 982 } 983 984 scope.finaliseVarAlloc(0) 985 } 986 987 func (c *compiler) compileDeclList(v []*ast.VariableDeclaration, inFunc bool) { 988 for _, value := range v { 989 c.createVarBindings(value, inFunc) 990 } 991 } 992 993 func (c *compiler) extractLabelled(st ast.Statement) ast.Statement { 994 if st, ok := st.(*ast.LabelledStatement); ok { 995 return c.extractLabelled(st.Statement) 996 } 997 return st 998 } 999 1000 func (c *compiler) extractFunctions(list []ast.Statement) (funcs []*ast.FunctionDeclaration) { 1001 for _, st := range list { 1002 var decl *ast.FunctionDeclaration 1003 switch st := c.extractLabelled(st).(type) { 1004 case *ast.FunctionDeclaration: 1005 decl = st 1006 case *ast.LabelledStatement: 1007 if st1, ok := st.Statement.(*ast.FunctionDeclaration); ok { 1008 decl = st1 1009 } else { 1010 continue 1011 } 1012 default: 1013 continue 1014 } 1015 funcs = append(funcs, decl) 1016 } 1017 return 1018 } 1019 1020 func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) { 1021 s := c.scope 1022 if s.outer != nil { 1023 unique := !s.isFunction() && !s.variable && s.strict 1024 if !unique { 1025 hasNonStandard := false 1026 for _, decl := range funcs { 1027 if !decl.Function.Async && !decl.Function.Generator { 1028 s.bindNameLexical(decl.Function.Name.Name, false, int(decl.Function.Name.Idx1())-1) 1029 } else { 1030 hasNonStandard = true 1031 } 1032 } 1033 if hasNonStandard { 1034 for _, decl := range funcs { 1035 if decl.Function.Async || decl.Function.Generator { 1036 s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1) 1037 } 1038 } 1039 } 1040 } else { 1041 for _, decl := range funcs { 1042 s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1) 1043 } 1044 } 1045 } else { 1046 for _, decl := range funcs { 1047 s.bindName(decl.Function.Name.Name) 1048 } 1049 } 1050 } 1051 1052 func (c *compiler) compileFunctions(list []*ast.FunctionDeclaration) { 1053 for _, decl := range list { 1054 c.compileFunction(decl) 1055 } 1056 } 1057 1058 func (c *compiler) compileFunctionsGlobalAllUnique(list []*ast.FunctionDeclaration) { 1059 for _, decl := range list { 1060 c.compileFunctionLiteral(decl.Function, false).emitGetter(true) 1061 } 1062 } 1063 1064 func (c *compiler) compileFunctionsGlobal(list []*ast.FunctionDeclaration) { 1065 m := make(map[unistring.String]int, len(list)) 1066 for i := len(list) - 1; i >= 0; i-- { 1067 name := list[i].Function.Name.Name 1068 if _, exists := m[name]; !exists { 1069 m[name] = i 1070 } 1071 } 1072 idx := 0 1073 for i, decl := range list { 1074 name := decl.Function.Name.Name 1075 if m[name] == i { 1076 c.compileFunctionLiteral(decl.Function, false).emitGetter(true) 1077 c.scope.bindings[idx] = c.scope.boundNames[name] 1078 idx++ 1079 } else { 1080 leave := c.enterDummyMode() 1081 c.compileFunctionLiteral(decl.Function, false).emitGetter(false) 1082 leave() 1083 } 1084 } 1085 } 1086 1087 func (c *compiler) createVarIdBinding(name unistring.String, offset int, inFunc bool) { 1088 if c.scope.strict { 1089 c.checkIdentifierLName(name, offset) 1090 c.checkIdentifierName(name, offset) 1091 } 1092 if !inFunc || name != "arguments" { 1093 c.scope.bindName(name) 1094 } 1095 } 1096 1097 func (c *compiler) createBindings(target ast.Expression, createIdBinding func(name unistring.String, offset int)) { 1098 switch target := target.(type) { 1099 case *ast.Identifier: 1100 createIdBinding(target.Name, int(target.Idx)-1) 1101 case *ast.ObjectPattern: 1102 for _, prop := range target.Properties { 1103 switch prop := prop.(type) { 1104 case *ast.PropertyShort: 1105 createIdBinding(prop.Name.Name, int(prop.Name.Idx)-1) 1106 case *ast.PropertyKeyed: 1107 c.createBindings(prop.Value, createIdBinding) 1108 default: 1109 c.throwSyntaxError(int(target.Idx0()-1), "unsupported property type in ObjectPattern: %T", prop) 1110 } 1111 } 1112 if target.Rest != nil { 1113 c.createBindings(target.Rest, createIdBinding) 1114 } 1115 case *ast.ArrayPattern: 1116 for _, elt := range target.Elements { 1117 if elt != nil { 1118 c.createBindings(elt, createIdBinding) 1119 } 1120 } 1121 if target.Rest != nil { 1122 c.createBindings(target.Rest, createIdBinding) 1123 } 1124 case *ast.AssignExpression: 1125 c.createBindings(target.Left, createIdBinding) 1126 default: 1127 c.throwSyntaxError(int(target.Idx0()-1), "unsupported binding target: %T", target) 1128 } 1129 } 1130 1131 func (c *compiler) createVarBinding(target ast.Expression, inFunc bool) { 1132 c.createBindings(target, func(name unistring.String, offset int) { 1133 c.createVarIdBinding(name, offset, inFunc) 1134 }) 1135 } 1136 1137 func (c *compiler) createVarBindings(v *ast.VariableDeclaration, inFunc bool) { 1138 for _, item := range v.List { 1139 c.createVarBinding(item.Target, inFunc) 1140 } 1141 } 1142 1143 func (c *compiler) createLexicalIdBinding(name unistring.String, isConst bool, offset int) *binding { 1144 if name == "let" { 1145 c.throwSyntaxError(offset, "let is disallowed as a lexically bound name") 1146 } 1147 if c.scope.strict { 1148 c.checkIdentifierLName(name, offset) 1149 c.checkIdentifierName(name, offset) 1150 } 1151 b, _ := c.scope.bindNameLexical(name, true, offset) 1152 if isConst { 1153 b.isConst, b.isStrict = true, true 1154 } 1155 return b 1156 } 1157 1158 func (c *compiler) createLexicalIdBindingFuncBody(name unistring.String, isConst bool, offset int, calleeBinding *binding) *binding { 1159 if name == "let" { 1160 c.throwSyntaxError(offset, "let is disallowed as a lexically bound name") 1161 } 1162 if c.scope.strict { 1163 c.checkIdentifierLName(name, offset) 1164 c.checkIdentifierName(name, offset) 1165 } 1166 paramScope := c.scope.outer 1167 parentBinding := paramScope.boundNames[name] 1168 if parentBinding != nil { 1169 if parentBinding != calleeBinding && (name != "arguments" || !paramScope.argsNeeded) { 1170 c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name) 1171 } 1172 } 1173 b, _ := c.scope.bindNameLexical(name, true, offset) 1174 if isConst { 1175 b.isConst, b.isStrict = true, true 1176 } 1177 return b 1178 } 1179 1180 func (c *compiler) createLexicalBinding(target ast.Expression, isConst bool) { 1181 c.createBindings(target, func(name unistring.String, offset int) { 1182 c.createLexicalIdBinding(name, isConst, offset) 1183 }) 1184 } 1185 1186 func (c *compiler) createLexicalBindings(lex *ast.LexicalDeclaration) { 1187 for _, d := range lex.List { 1188 c.createLexicalBinding(d.Target, lex.Token == token.CONST) 1189 } 1190 } 1191 1192 func (c *compiler) compileLexicalDeclarations(list []ast.Statement, scopeDeclared bool) bool { 1193 for _, st := range list { 1194 if lex, ok := st.(*ast.LexicalDeclaration); ok { 1195 if !scopeDeclared { 1196 c.newBlockScope() 1197 scopeDeclared = true 1198 } 1199 c.createLexicalBindings(lex) 1200 } else if cls, ok := st.(*ast.ClassDeclaration); ok { 1201 if !scopeDeclared { 1202 c.newBlockScope() 1203 scopeDeclared = true 1204 } 1205 c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1) 1206 } 1207 } 1208 return scopeDeclared 1209 } 1210 1211 func (c *compiler) compileLexicalDeclarationsFuncBody(list []ast.Statement, calleeBinding *binding) { 1212 for _, st := range list { 1213 if lex, ok := st.(*ast.LexicalDeclaration); ok { 1214 isConst := lex.Token == token.CONST 1215 for _, d := range lex.List { 1216 c.createBindings(d.Target, func(name unistring.String, offset int) { 1217 c.createLexicalIdBindingFuncBody(name, isConst, offset, calleeBinding) 1218 }) 1219 } 1220 } else if cls, ok := st.(*ast.ClassDeclaration); ok { 1221 c.createLexicalIdBindingFuncBody(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1, calleeBinding) 1222 } 1223 } 1224 } 1225 1226 func (c *compiler) compileFunction(v *ast.FunctionDeclaration) { 1227 name := v.Function.Name.Name 1228 b := c.scope.boundNames[name] 1229 if b == nil || b.isVar { 1230 e := &compiledIdentifierExpr{ 1231 name: v.Function.Name.Name, 1232 } 1233 e.init(c, v.Function.Idx0()) 1234 e.emitSetter(c.compileFunctionLiteral(v.Function, false), false) 1235 } else { 1236 c.compileFunctionLiteral(v.Function, false).emitGetter(true) 1237 b.emitInitP() 1238 } 1239 } 1240 1241 func (c *compiler) compileStandaloneFunctionDecl(v *ast.FunctionDeclaration) { 1242 if v.Function.Async { 1243 c.throwSyntaxError(int(v.Idx0())-1, "Async functions can only be declared at top level or inside a block.") 1244 } 1245 if v.Function.Generator { 1246 c.throwSyntaxError(int(v.Idx0())-1, "Generators can only be declared at top level or inside a block.") 1247 } 1248 if c.scope.strict { 1249 c.throwSyntaxError(int(v.Idx0())-1, "In strict mode code, functions can only be declared at top level or inside a block.") 1250 } 1251 c.throwSyntaxError(int(v.Idx0())-1, "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.") 1252 } 1253 1254 func (c *compiler) emit(instructions ...instruction) { 1255 c.p.code = append(c.p.code, instructions...) 1256 } 1257 1258 func (c *compiler) throwSyntaxError(offset int, format string, args ...interface{}) { 1259 panic(&CompilerSyntaxError{ 1260 CompilerError: CompilerError{ 1261 File: c.p.src, 1262 Offset: offset, 1263 Message: fmt.Sprintf(format, args...), 1264 }, 1265 }) 1266 } 1267 1268 func (c *compiler) isStrict(list []ast.Statement) *ast.StringLiteral { 1269 for _, st := range list { 1270 if st, ok := st.(*ast.ExpressionStatement); ok { 1271 if e, ok := st.Expression.(*ast.StringLiteral); ok { 1272 if e.Literal == `"use strict"` || e.Literal == `'use strict'` { 1273 return e 1274 } 1275 } else { 1276 break 1277 } 1278 } else { 1279 break 1280 } 1281 } 1282 return nil 1283 } 1284 1285 func (c *compiler) isStrictStatement(s ast.Statement) *ast.StringLiteral { 1286 if s, ok := s.(*ast.BlockStatement); ok { 1287 return c.isStrict(s.List) 1288 } 1289 return nil 1290 } 1291 1292 func (c *compiler) checkIdentifierName(name unistring.String, offset int) { 1293 switch name { 1294 case "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield": 1295 c.throwSyntaxError(offset, "Unexpected strict mode reserved word") 1296 } 1297 } 1298 1299 func (c *compiler) checkIdentifierLName(name unistring.String, offset int) { 1300 switch name { 1301 case "eval", "arguments": 1302 c.throwSyntaxError(offset, "Assignment to eval or arguments is not allowed in strict mode") 1303 } 1304 } 1305 1306 // Enter a 'dummy' compilation mode. Any code produced after this method is called will be discarded after 1307 // leaveFunc is called with no additional side effects. This is useful for compiling code inside a 1308 // constant falsy condition 'if' branch or a loop (i.e 'if (false) { ... } or while (false) { ... }). 1309 // Such code should not be included in the final compilation result as it's never called, but it must 1310 // still produce compilation errors if there are any. 1311 // TODO: make sure variable lookups do not de-optimise parent scopes 1312 func (c *compiler) enterDummyMode() (leaveFunc func()) { 1313 savedBlock, savedProgram := c.block, c.p 1314 if savedBlock != nil { 1315 c.block = &block{ 1316 typ: savedBlock.typ, 1317 label: savedBlock.label, 1318 outer: savedBlock.outer, 1319 breaking: savedBlock.breaking, 1320 } 1321 } 1322 c.p = &Program{ 1323 src: c.p.src, 1324 } 1325 c.newScope() 1326 return func() { 1327 c.block, c.p = savedBlock, savedProgram 1328 c.popScope() 1329 } 1330 } 1331 1332 func (c *compiler) compileStatementDummy(statement ast.Statement) { 1333 leave := c.enterDummyMode() 1334 c.compileStatement(statement, false) 1335 leave() 1336 } 1337 1338 func (c *compiler) assert(cond bool, offset int, msg string, args ...interface{}) { 1339 if !cond { 1340 c.throwSyntaxError(offset, "Compiler bug: "+msg, args...) 1341 } 1342 } 1343 1344 func privateIdString(desc unistring.String) unistring.String { 1345 return asciiString("#").Concat(stringValueFromRaw(desc)).string() 1346 } 1347 1348 type privateName struct { 1349 idx int 1350 isStatic bool 1351 isMethod bool 1352 hasGetter, hasSetter bool 1353 } 1354 1355 type resolvedPrivateName struct { 1356 name unistring.String 1357 idx uint32 1358 level uint8 1359 isStatic bool 1360 isMethod bool 1361 } 1362 1363 func (r *resolvedPrivateName) string() unistring.String { 1364 return privateIdString(r.name) 1365 } 1366 1367 type privateEnvRegistry struct { 1368 fields, methods []unistring.String 1369 } 1370 1371 type classScope struct { 1372 c *compiler 1373 privateNames map[unistring.String]*privateName 1374 1375 instanceEnv, staticEnv privateEnvRegistry 1376 1377 outer *classScope 1378 } 1379 1380 func (r *privateEnvRegistry) createPrivateMethodId(name unistring.String) int { 1381 r.methods = append(r.methods, name) 1382 return len(r.methods) - 1 1383 } 1384 1385 func (r *privateEnvRegistry) createPrivateFieldId(name unistring.String) int { 1386 r.fields = append(r.fields, name) 1387 return len(r.fields) - 1 1388 } 1389 1390 func (s *classScope) declarePrivateId(name unistring.String, kind ast.PropertyKind, isStatic bool, offset int) { 1391 pn := s.privateNames[name] 1392 if pn != nil { 1393 if pn.isStatic == isStatic { 1394 switch kind { 1395 case ast.PropertyKindGet: 1396 if pn.hasSetter && !pn.hasGetter { 1397 pn.hasGetter = true 1398 return 1399 } 1400 case ast.PropertyKindSet: 1401 if pn.hasGetter && !pn.hasSetter { 1402 pn.hasSetter = true 1403 return 1404 } 1405 } 1406 } 1407 s.c.throwSyntaxError(offset, "Identifier '#%s' has already been declared", name) 1408 panic("unreachable") 1409 } 1410 var env *privateEnvRegistry 1411 if isStatic { 1412 env = &s.staticEnv 1413 } else { 1414 env = &s.instanceEnv 1415 } 1416 1417 pn = &privateName{ 1418 isStatic: isStatic, 1419 hasGetter: kind == ast.PropertyKindGet, 1420 hasSetter: kind == ast.PropertyKindSet, 1421 } 1422 if kind != ast.PropertyKindValue { 1423 pn.idx = env.createPrivateMethodId(name) 1424 pn.isMethod = true 1425 } else { 1426 pn.idx = env.createPrivateFieldId(name) 1427 } 1428 1429 if s.privateNames == nil { 1430 s.privateNames = make(map[unistring.String]*privateName) 1431 } 1432 s.privateNames[name] = pn 1433 } 1434 1435 func (s *classScope) getDeclaredPrivateId(name unistring.String) *privateName { 1436 if n := s.privateNames[name]; n != nil { 1437 return n 1438 } 1439 s.c.assert(false, 0, "getDeclaredPrivateId() for undeclared id") 1440 panic("unreachable") 1441 } 1442 1443 func (c *compiler) resolvePrivateName(name unistring.String, offset int) (*resolvedPrivateName, *privateId) { 1444 level := 0 1445 for s := c.classScope; s != nil; s = s.outer { 1446 if len(s.privateNames) > 0 { 1447 if pn := s.privateNames[name]; pn != nil { 1448 return &resolvedPrivateName{ 1449 name: name, 1450 idx: uint32(pn.idx), 1451 level: uint8(level), 1452 isStatic: pn.isStatic, 1453 isMethod: pn.isMethod, 1454 }, nil 1455 } 1456 level++ 1457 } 1458 } 1459 if c.ctxVM != nil { 1460 for s := c.ctxVM.privEnv; s != nil; s = s.outer { 1461 if id := s.names[name]; id != nil { 1462 return nil, id 1463 } 1464 } 1465 } 1466 c.throwSyntaxError(offset, "Private field '#%s' must be declared in an enclosing class", name) 1467 panic("unreachable") 1468 }