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