12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487 |
- package goja
- import (
- "fmt"
- "github.com/dop251/goja/token"
- "sort"
- "github.com/dop251/goja/ast"
- "github.com/dop251/goja/file"
- "github.com/dop251/goja/unistring"
- )
- type blockType int
- const (
- blockLoop blockType = iota
- blockLoopEnum
- blockTry
- blockLabel
- blockSwitch
- blockWith
- blockScope
- blockIterScope
- blockOptChain
- )
- const (
- maskConst = 1 << 31
- maskVar = 1 << 30
- maskDeletable = 1 << 29
- maskStrict = maskDeletable
- maskTyp = maskConst | maskVar | maskDeletable
- )
- type varType byte
- const (
- varTypeVar varType = iota
- varTypeLet
- varTypeStrictConst
- varTypeConst
- )
- const thisBindingName = " this" // must not be a valid identifier
- type CompilerError struct {
- Message string
- File *file.File
- Offset int
- }
- type CompilerSyntaxError struct {
- CompilerError
- }
- type CompilerReferenceError struct {
- CompilerError
- }
- type srcMapItem struct {
- pc int
- srcPos int
- }
- // Program is an internal, compiled representation of code which is produced by the Compile function.
- // This representation is not linked to a runtime in any way and can be used concurrently.
- // It is always preferable to use a Program over a string when running code as it skips the compilation step.
- type Program struct {
- code []instruction
- funcName unistring.String
- src *file.File
- srcMap []srcMapItem
- }
- type compiler struct {
- p *Program
- scope *scope
- block *block
- classScope *classScope
- enumGetExpr compiledEnumGetExpr
- evalVM *vm // VM used to evaluate constant expressions
- ctxVM *vm // VM in which an eval() code is compiled
- codeScratchpad []instruction
- stringCache map[unistring.String]Value
- }
- type binding struct {
- scope *scope
- name unistring.String
- accessPoints map[*scope]*[]int
- isConst bool
- isStrict bool
- isArg bool
- isVar bool
- inStash bool
- }
- func (b *binding) getAccessPointsForScope(s *scope) *[]int {
- m := b.accessPoints[s]
- if m == nil {
- a := make([]int, 0, 1)
- m = &a
- if b.accessPoints == nil {
- b.accessPoints = make(map[*scope]*[]int)
- }
- b.accessPoints[s] = m
- }
- return m
- }
- func (b *binding) markAccessPointAt(pos int) {
- scope := b.scope.c.scope
- m := b.getAccessPointsForScope(scope)
- *m = append(*m, pos-scope.base)
- }
- func (b *binding) markAccessPointAtScope(scope *scope, pos int) {
- m := b.getAccessPointsForScope(scope)
- *m = append(*m, pos-scope.base)
- }
- func (b *binding) markAccessPoint() {
- scope := b.scope.c.scope
- m := b.getAccessPointsForScope(scope)
- *m = append(*m, len(scope.prg.code)-scope.base)
- }
- func (b *binding) emitGet() {
- b.markAccessPoint()
- if b.isVar && !b.isArg {
- b.scope.c.emit(loadStack(0))
- } else {
- b.scope.c.emit(loadStackLex(0))
- }
- }
- func (b *binding) emitGetAt(pos int) {
- b.markAccessPointAt(pos)
- if b.isVar && !b.isArg {
- b.scope.c.p.code[pos] = loadStack(0)
- } else {
- b.scope.c.p.code[pos] = loadStackLex(0)
- }
- }
- func (b *binding) emitGetP() {
- if b.isVar && !b.isArg {
- // no-op
- } else {
- // make sure TDZ is checked
- b.markAccessPoint()
- b.scope.c.emit(loadStackLex(0), pop)
- }
- }
- func (b *binding) emitSet() {
- if b.isConst {
- if b.isStrict || b.scope.c.scope.strict {
- b.scope.c.emit(throwAssignToConst)
- }
- return
- }
- b.markAccessPoint()
- if b.isVar && !b.isArg {
- b.scope.c.emit(storeStack(0))
- } else {
- b.scope.c.emit(storeStackLex(0))
- }
- }
- func (b *binding) emitSetP() {
- if b.isConst {
- if b.isStrict || b.scope.c.scope.strict {
- b.scope.c.emit(throwAssignToConst)
- }
- return
- }
- b.markAccessPoint()
- if b.isVar && !b.isArg {
- b.scope.c.emit(storeStackP(0))
- } else {
- b.scope.c.emit(storeStackLexP(0))
- }
- }
- func (b *binding) emitInitP() {
- if !b.isVar && b.scope.outer == nil {
- b.scope.c.emit(initGlobalP(b.name))
- } else {
- b.markAccessPoint()
- b.scope.c.emit(initStackP(0))
- }
- }
- func (b *binding) emitInit() {
- if !b.isVar && b.scope.outer == nil {
- b.scope.c.emit(initGlobal(b.name))
- } else {
- b.markAccessPoint()
- b.scope.c.emit(initStack(0))
- }
- }
- func (b *binding) emitInitAt(pos int) {
- if !b.isVar && b.scope.outer == nil {
- b.scope.c.p.code[pos] = initGlobal(b.name)
- } else {
- b.markAccessPointAt(pos)
- b.scope.c.p.code[pos] = initStack(0)
- }
- }
- func (b *binding) emitInitAtScope(scope *scope, pos int) {
- if !b.isVar && scope.outer == nil {
- scope.c.p.code[pos] = initGlobal(b.name)
- } else {
- b.markAccessPointAtScope(scope, pos)
- scope.c.p.code[pos] = initStack(0)
- }
- }
- func (b *binding) emitInitPAtScope(scope *scope, pos int) {
- if !b.isVar && scope.outer == nil {
- scope.c.p.code[pos] = initGlobalP(b.name)
- } else {
- b.markAccessPointAtScope(scope, pos)
- scope.c.p.code[pos] = initStackP(0)
- }
- }
- func (b *binding) emitGetVar(callee bool) {
- b.markAccessPoint()
- if b.isVar && !b.isArg {
- b.scope.c.emit(&loadMixed{name: b.name, callee: callee})
- } else {
- b.scope.c.emit(&loadMixedLex{name: b.name, callee: callee})
- }
- }
- func (b *binding) emitResolveVar(strict bool) {
- b.markAccessPoint()
- if b.isVar && !b.isArg {
- b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: varTypeVar})
- } else {
- var typ varType
- if b.isConst {
- if b.isStrict {
- typ = varTypeStrictConst
- } else {
- typ = varTypeConst
- }
- } else {
- typ = varTypeLet
- }
- b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: typ})
- }
- }
- func (b *binding) moveToStash() {
- if b.isArg && !b.scope.argsInStash {
- b.scope.moveArgsToStash()
- } else {
- b.inStash = true
- b.scope.needStash = true
- }
- }
- func (b *binding) useCount() (count int) {
- for _, a := range b.accessPoints {
- count += len(*a)
- }
- return
- }
- type scope struct {
- c *compiler
- prg *Program
- outer *scope
- nested []*scope
- boundNames map[unistring.String]*binding
- bindings []*binding
- base int
- numArgs int
- // function type. If not funcNone, this is a function or a top-level lexical environment
- funcType funcType
- // in strict mode
- strict bool
- // eval top-level scope
- eval bool
- // at least one inner scope has direct eval() which can lookup names dynamically (by name)
- dynLookup bool
- // at least one binding has been marked for placement in stash
- needStash bool
- // is a variable environment, i.e. the target for dynamically created var bindings
- variable bool
- // a function scope that has at least one direct eval() and non-strict, so the variables can be added dynamically
- dynamic bool
- // arguments have been marked for placement in stash (functions only)
- argsInStash bool
- // need 'arguments' object (functions only)
- argsNeeded bool
- }
- type block struct {
- typ blockType
- label unistring.String
- cont int
- breaks []int
- conts []int
- outer *block
- breaking *block // set when the 'finally' block is an empty break statement sequence
- needResult bool
- }
- func (c *compiler) leaveScopeBlock(enter *enterBlock) {
- c.updateEnterBlock(enter)
- leave := &leaveBlock{
- stackSize: enter.stackSize,
- popStash: enter.stashSize > 0,
- }
- c.emit(leave)
- for _, pc := range c.block.breaks {
- c.p.code[pc] = leave
- }
- c.block.breaks = nil
- c.leaveBlock()
- }
- func (c *compiler) leaveBlock() {
- lbl := len(c.p.code)
- for _, item := range c.block.breaks {
- c.p.code[item] = jump(lbl - item)
- }
- if t := c.block.typ; t == blockLoop || t == blockLoopEnum {
- for _, item := range c.block.conts {
- c.p.code[item] = jump(c.block.cont - item)
- }
- }
- c.block = c.block.outer
- }
- func (e *CompilerSyntaxError) Error() string {
- if e.File != nil {
- return fmt.Sprintf("SyntaxError: %s at %s", e.Message, e.File.Position(e.Offset))
- }
- return fmt.Sprintf("SyntaxError: %s", e.Message)
- }
- func (e *CompilerReferenceError) Error() string {
- return fmt.Sprintf("ReferenceError: %s", e.Message)
- }
- func (c *compiler) newScope() {
- strict := false
- if c.scope != nil {
- strict = c.scope.strict
- }
- c.scope = &scope{
- c: c,
- prg: c.p,
- outer: c.scope,
- strict: strict,
- }
- }
- func (c *compiler) newBlockScope() {
- c.newScope()
- if outer := c.scope.outer; outer != nil {
- outer.nested = append(outer.nested, c.scope)
- }
- c.scope.base = len(c.p.code)
- }
- func (c *compiler) popScope() {
- c.scope = c.scope.outer
- }
- func (c *compiler) emitLiteralString(s String) {
- key := s.string()
- if c.stringCache == nil {
- c.stringCache = make(map[unistring.String]Value)
- }
- internVal := c.stringCache[key]
- if internVal == nil {
- c.stringCache[key] = s
- internVal = s
- }
- c.emit(loadVal{internVal})
- }
- func (c *compiler) emitLiteralValue(v Value) {
- if s, ok := v.(String); ok {
- c.emitLiteralString(s)
- return
- }
- c.emit(loadVal{v})
- }
- func newCompiler() *compiler {
- c := &compiler{
- p: &Program{},
- }
- c.enumGetExpr.init(c, file.Idx(0))
- return c
- }
- func (p *Program) dumpCode(logger func(format string, args ...interface{})) {
- p._dumpCode("", logger)
- }
- func (p *Program) _dumpCode(indent string, logger func(format string, args ...interface{})) {
- dumpInitFields := func(initFields *Program) {
- i := indent + ">"
- logger("%s ---- init_fields:", i)
- initFields._dumpCode(i, logger)
- logger("%s ----", i)
- }
- for pc, ins := range p.code {
- logger("%s %d: %T(%v)", indent, pc, ins, ins)
- var prg *Program
- switch f := ins.(type) {
- case newFuncInstruction:
- prg = f.getPrg()
- case *newDerivedClass:
- if f.initFields != nil {
- dumpInitFields(f.initFields)
- }
- prg = f.ctor
- case *newClass:
- if f.initFields != nil {
- dumpInitFields(f.initFields)
- }
- prg = f.ctor
- case *newStaticFieldInit:
- if f.initFields != nil {
- dumpInitFields(f.initFields)
- }
- }
- if prg != nil {
- prg._dumpCode(indent+">", logger)
- }
- }
- }
- func (p *Program) sourceOffset(pc int) int {
- i := sort.Search(len(p.srcMap), func(idx int) bool {
- return p.srcMap[idx].pc > pc
- }) - 1
- if i >= 0 {
- return p.srcMap[i].srcPos
- }
- return 0
- }
- func (p *Program) addSrcMap(srcPos int) {
- if len(p.srcMap) > 0 && p.srcMap[len(p.srcMap)-1].srcPos == srcPos {
- return
- }
- p.srcMap = append(p.srcMap, srcMapItem{pc: len(p.code), srcPos: srcPos})
- }
- func (s *scope) lookupName(name unistring.String) (binding *binding, noDynamics bool) {
- noDynamics = true
- toStash := false
- for curScope := s; ; curScope = curScope.outer {
- if curScope.outer != nil {
- if b, exists := curScope.boundNames[name]; exists {
- if toStash && !b.inStash {
- b.moveToStash()
- }
- binding = b
- return
- }
- } else {
- noDynamics = false
- return
- }
- if curScope.dynamic {
- noDynamics = false
- }
- if name == "arguments" && curScope.funcType != funcNone && curScope.funcType != funcArrow {
- if curScope.funcType == funcClsInit {
- s.c.throwSyntaxError(0, "'arguments' is not allowed in class field initializer or static initialization block")
- }
- curScope.argsNeeded = true
- binding, _ = curScope.bindName(name)
- return
- }
- if curScope.isFunction() {
- toStash = true
- }
- }
- }
- func (s *scope) lookupThis() (*binding, bool) {
- toStash := false
- for curScope := s; curScope != nil; curScope = curScope.outer {
- if curScope.outer == nil {
- if curScope.eval {
- return nil, true
- }
- }
- if b, exists := curScope.boundNames[thisBindingName]; exists {
- if toStash && !b.inStash {
- b.moveToStash()
- }
- return b, false
- }
- if curScope.isFunction() {
- toStash = true
- }
- }
- return nil, false
- }
- func (s *scope) ensureBoundNamesCreated() {
- if s.boundNames == nil {
- s.boundNames = make(map[unistring.String]*binding)
- }
- }
- func (s *scope) addBinding(offset int) *binding {
- if len(s.bindings) >= (1<<24)-1 {
- s.c.throwSyntaxError(offset, "Too many variables")
- }
- b := &binding{
- scope: s,
- }
- s.bindings = append(s.bindings, b)
- return b
- }
- func (s *scope) bindNameLexical(name unistring.String, unique bool, offset int) (*binding, bool) {
- if b := s.boundNames[name]; b != nil {
- if unique {
- s.c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
- }
- return b, false
- }
- b := s.addBinding(offset)
- b.name = name
- s.ensureBoundNamesCreated()
- s.boundNames[name] = b
- return b, true
- }
- func (s *scope) createThisBinding() *binding {
- thisBinding, _ := s.bindNameLexical(thisBindingName, false, 0)
- thisBinding.isVar = true // don't check on load
- return thisBinding
- }
- func (s *scope) bindName(name unistring.String) (*binding, bool) {
- if !s.isFunction() && !s.variable && s.outer != nil {
- return s.outer.bindName(name)
- }
- b, created := s.bindNameLexical(name, false, 0)
- if created {
- b.isVar = true
- }
- return b, created
- }
- func (s *scope) bindNameShadow(name unistring.String) (*binding, bool) {
- if !s.isFunction() && s.outer != nil {
- return s.outer.bindNameShadow(name)
- }
- _, exists := s.boundNames[name]
- b := &binding{
- scope: s,
- name: name,
- }
- s.bindings = append(s.bindings, b)
- s.ensureBoundNamesCreated()
- s.boundNames[name] = b
- return b, !exists
- }
- func (s *scope) nearestFunction() *scope {
- for sc := s; sc != nil; sc = sc.outer {
- if sc.isFunction() {
- return sc
- }
- }
- return nil
- }
- func (s *scope) nearestThis() *scope {
- for sc := s; sc != nil; sc = sc.outer {
- if sc.eval || sc.isFunction() && sc.funcType != funcArrow {
- return sc
- }
- }
- return nil
- }
- func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
- argsInStash := false
- if f := s.nearestFunction(); f != nil {
- argsInStash = f.argsInStash
- }
- stackIdx, stashIdx := 0, 0
- allInStash := s.isDynamic()
- var derivedCtor bool
- if fs := s.nearestThis(); fs != nil && fs.funcType == funcDerivedCtor {
- derivedCtor = true
- }
- for i, b := range s.bindings {
- var this bool
- if b.name == thisBindingName {
- this = true
- }
- if allInStash || b.inStash {
- for scope, aps := range b.accessPoints {
- var level uint32
- for sc := scope; sc != nil && sc != s; sc = sc.outer {
- if sc.needStash || sc.isDynamic() {
- level++
- }
- }
- if level > 255 {
- s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded")
- }
- idx := (level << 24) | uint32(stashIdx)
- base := scope.base
- code := scope.prg.code
- if this {
- if derivedCtor {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch (*ap).(type) {
- case loadStack:
- *ap = loadThisStash(idx)
- case initStack:
- *ap = initStash(idx)
- case initStackP:
- *ap = initStashP(idx)
- case resolveThisStack:
- *ap = resolveThisStash(idx)
- case _ret:
- *ap = cret(idx)
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
- }
- }
- } else {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch (*ap).(type) {
- case loadStack:
- *ap = loadStash(idx)
- case initStack:
- *ap = initStash(idx)
- case initStackP:
- *ap = initStashP(idx)
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
- }
- }
- }
- } else {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch i := (*ap).(type) {
- case loadStack:
- *ap = loadStash(idx)
- case storeStack:
- *ap = storeStash(idx)
- case storeStackP:
- *ap = storeStashP(idx)
- case loadStackLex:
- *ap = loadStashLex(idx)
- case storeStackLex:
- *ap = storeStashLex(idx)
- case storeStackLexP:
- *ap = storeStashLexP(idx)
- case initStackP:
- *ap = initStashP(idx)
- case initStack:
- *ap = initStash(idx)
- case *loadMixed:
- i.idx = idx
- case *loadMixedLex:
- i.idx = idx
- case *resolveMixed:
- i.idx = idx
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
- }
- }
- }
- }
- stashIdx++
- } else {
- var idx int
- if !this {
- if i < s.numArgs {
- idx = -(i + 1)
- } else {
- stackIdx++
- idx = stackIdx + stackOffset
- }
- }
- for scope, aps := range b.accessPoints {
- var level int
- for sc := scope; sc != nil && sc != s; sc = sc.outer {
- if sc.needStash || sc.isDynamic() {
- level++
- }
- }
- if level > 255 {
- s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded")
- }
- code := scope.prg.code
- base := scope.base
- if this {
- if derivedCtor {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch (*ap).(type) {
- case loadStack:
- *ap = loadThisStack{}
- case initStack:
- // no-op
- case initStackP:
- // no-op
- case resolveThisStack:
- // no-op
- case _ret:
- // no-op, already in the right place
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
- }
- }
- } /*else {
- no-op
- }*/
- } else if argsInStash {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch i := (*ap).(type) {
- case loadStack:
- *ap = loadStack1(idx)
- case storeStack:
- *ap = storeStack1(idx)
- case storeStackP:
- *ap = storeStack1P(idx)
- case loadStackLex:
- *ap = loadStack1Lex(idx)
- case storeStackLex:
- *ap = storeStack1Lex(idx)
- case storeStackLexP:
- *ap = storeStack1LexP(idx)
- case initStackP:
- *ap = initStack1P(idx)
- case initStack:
- *ap = initStack1(idx)
- case *loadMixed:
- *ap = &loadMixedStack1{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
- case *loadMixedLex:
- *ap = &loadMixedStack1Lex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
- case *resolveMixed:
- *ap = &resolveMixedStack1{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
- }
- }
- } else {
- for _, pc := range *aps {
- ap := &code[base+pc]
- switch i := (*ap).(type) {
- case loadStack:
- *ap = loadStack(idx)
- case storeStack:
- *ap = storeStack(idx)
- case storeStackP:
- *ap = storeStackP(idx)
- case loadStackLex:
- *ap = loadStackLex(idx)
- case storeStackLex:
- *ap = storeStackLex(idx)
- case storeStackLexP:
- *ap = storeStackLexP(idx)
- case initStack:
- *ap = initStack(idx)
- case initStackP:
- *ap = initStackP(idx)
- case *loadMixed:
- *ap = &loadMixedStack{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
- case *loadMixedLex:
- *ap = &loadMixedStackLex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
- case *resolveMixed:
- *ap = &resolveMixedStack{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
- default:
- s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
- }
- }
- }
- }
- }
- }
- for _, nested := range s.nested {
- nested.finaliseVarAlloc(stackIdx + stackOffset)
- }
- return stashIdx, stackIdx
- }
- func (s *scope) moveArgsToStash() {
- for _, b := range s.bindings {
- if !b.isArg {
- break
- }
- b.inStash = true
- }
- s.argsInStash = true
- s.needStash = true
- }
- func (c *compiler) trimCode(delta int) {
- src := c.p.code[delta:]
- newCode := make([]instruction, len(src))
- copy(newCode, src)
- if cap(c.codeScratchpad) < cap(c.p.code) {
- c.codeScratchpad = c.p.code[:0]
- }
- c.p.code = newCode
- }
- func (s *scope) trimCode(delta int) {
- s.c.trimCode(delta)
- if delta != 0 {
- srcMap := s.c.p.srcMap
- for i := range srcMap {
- srcMap[i].pc -= delta
- }
- s.adjustBase(-delta)
- }
- }
- func (s *scope) adjustBase(delta int) {
- s.base += delta
- for _, nested := range s.nested {
- nested.adjustBase(delta)
- }
- }
- func (s *scope) makeNamesMap() map[unistring.String]uint32 {
- l := len(s.bindings)
- if l == 0 {
- return nil
- }
- names := make(map[unistring.String]uint32, l)
- for i, b := range s.bindings {
- idx := uint32(i)
- if b.isConst {
- idx |= maskConst
- if b.isStrict {
- idx |= maskStrict
- }
- }
- if b.isVar {
- idx |= maskVar
- }
- names[b.name] = idx
- }
- return names
- }
- func (s *scope) isDynamic() bool {
- return s.dynLookup || s.dynamic
- }
- func (s *scope) isFunction() bool {
- return s.funcType != funcNone && !s.eval
- }
- func (s *scope) deleteBinding(b *binding) {
- idx := 0
- for i, bb := range s.bindings {
- if bb == b {
- idx = i
- goto found
- }
- }
- return
- found:
- delete(s.boundNames, b.name)
- copy(s.bindings[idx:], s.bindings[idx+1:])
- l := len(s.bindings) - 1
- s.bindings[l] = nil
- s.bindings = s.bindings[:l]
- }
- func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) {
- c.ctxVM = evalVm
- eval := evalVm != nil
- c.p.src = in.File
- c.newScope()
- scope := c.scope
- scope.dynamic = true
- scope.eval = eval
- if !strict && len(in.Body) > 0 {
- strict = c.isStrict(in.Body) != nil
- }
- scope.strict = strict
- ownVarScope := eval && strict
- ownLexScope := !inGlobal || eval
- if ownVarScope {
- c.newBlockScope()
- scope = c.scope
- scope.variable = true
- }
- if eval && !inGlobal {
- for s := evalVm.stash; s != nil; s = s.outer {
- if ft := s.funcType; ft != funcNone && ft != funcArrow {
- scope.funcType = ft
- break
- }
- }
- }
- funcs := c.extractFunctions(in.Body)
- c.createFunctionBindings(funcs)
- numFuncs := len(scope.bindings)
- if inGlobal && !ownVarScope {
- if numFuncs == len(funcs) {
- c.compileFunctionsGlobalAllUnique(funcs)
- } else {
- c.compileFunctionsGlobal(funcs)
- }
- }
- c.compileDeclList(in.DeclarationList, false)
- numVars := len(scope.bindings) - numFuncs
- vars := make([]unistring.String, len(scope.bindings))
- for i, b := range scope.bindings {
- vars[i] = b.name
- }
- if len(vars) > 0 && !ownVarScope && ownLexScope {
- if inGlobal {
- c.emit(&bindGlobal{
- vars: vars[numFuncs:],
- funcs: vars[:numFuncs],
- deletable: eval,
- })
- } else {
- c.emit(&bindVars{names: vars, deletable: eval})
- }
- }
- var enter *enterBlock
- if c.compileLexicalDeclarations(in.Body, ownVarScope || !ownLexScope) {
- if ownLexScope {
- c.block = &block{
- outer: c.block,
- typ: blockScope,
- needResult: true,
- }
- enter = &enterBlock{}
- c.emit(enter)
- }
- }
- if len(scope.bindings) > 0 && !ownLexScope {
- var lets, consts []unistring.String
- for _, b := range c.scope.bindings[numFuncs+numVars:] {
- if b.isConst {
- consts = append(consts, b.name)
- } else {
- lets = append(lets, b.name)
- }
- }
- c.emit(&bindGlobal{
- vars: vars[numFuncs:],
- funcs: vars[:numFuncs],
- lets: lets,
- consts: consts,
- })
- }
- if !inGlobal || ownVarScope {
- c.compileFunctions(funcs)
- }
- c.compileStatements(in.Body, true)
- if enter != nil {
- c.leaveScopeBlock(enter)
- c.popScope()
- }
- scope.finaliseVarAlloc(0)
- c.stringCache = nil
- }
- func (c *compiler) compileDeclList(v []*ast.VariableDeclaration, inFunc bool) {
- for _, value := range v {
- c.createVarBindings(value, inFunc)
- }
- }
- func (c *compiler) extractLabelled(st ast.Statement) ast.Statement {
- if st, ok := st.(*ast.LabelledStatement); ok {
- return c.extractLabelled(st.Statement)
- }
- return st
- }
- func (c *compiler) extractFunctions(list []ast.Statement) (funcs []*ast.FunctionDeclaration) {
- for _, st := range list {
- var decl *ast.FunctionDeclaration
- switch st := c.extractLabelled(st).(type) {
- case *ast.FunctionDeclaration:
- decl = st
- case *ast.LabelledStatement:
- if st1, ok := st.Statement.(*ast.FunctionDeclaration); ok {
- decl = st1
- } else {
- continue
- }
- default:
- continue
- }
- funcs = append(funcs, decl)
- }
- return
- }
- func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) {
- s := c.scope
- if s.outer != nil {
- unique := !s.isFunction() && !s.variable && s.strict
- if !unique {
- hasNonStandard := false
- for _, decl := range funcs {
- if !decl.Function.Async && !decl.Function.Generator {
- s.bindNameLexical(decl.Function.Name.Name, false, int(decl.Function.Name.Idx1())-1)
- } else {
- hasNonStandard = true
- }
- }
- if hasNonStandard {
- for _, decl := range funcs {
- if decl.Function.Async || decl.Function.Generator {
- s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
- }
- }
- }
- } else {
- for _, decl := range funcs {
- s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
- }
- }
- } else {
- for _, decl := range funcs {
- s.bindName(decl.Function.Name.Name)
- }
- }
- }
- func (c *compiler) compileFunctions(list []*ast.FunctionDeclaration) {
- for _, decl := range list {
- c.compileFunction(decl)
- }
- }
- func (c *compiler) compileFunctionsGlobalAllUnique(list []*ast.FunctionDeclaration) {
- for _, decl := range list {
- c.compileFunctionLiteral(decl.Function, false).emitGetter(true)
- }
- }
- func (c *compiler) compileFunctionsGlobal(list []*ast.FunctionDeclaration) {
- m := make(map[unistring.String]int, len(list))
- for i := len(list) - 1; i >= 0; i-- {
- name := list[i].Function.Name.Name
- if _, exists := m[name]; !exists {
- m[name] = i
- }
- }
- idx := 0
- for i, decl := range list {
- name := decl.Function.Name.Name
- if m[name] == i {
- c.compileFunctionLiteral(decl.Function, false).emitGetter(true)
- c.scope.bindings[idx] = c.scope.boundNames[name]
- idx++
- } else {
- leave := c.enterDummyMode()
- c.compileFunctionLiteral(decl.Function, false).emitGetter(false)
- leave()
- }
- }
- }
- func (c *compiler) createVarIdBinding(name unistring.String, offset int, inFunc bool) {
- if c.scope.strict {
- c.checkIdentifierLName(name, offset)
- c.checkIdentifierName(name, offset)
- }
- if !inFunc || name != "arguments" {
- c.scope.bindName(name)
- }
- }
- func (c *compiler) createBindings(target ast.Expression, createIdBinding func(name unistring.String, offset int)) {
- switch target := target.(type) {
- case *ast.Identifier:
- createIdBinding(target.Name, int(target.Idx)-1)
- case *ast.ObjectPattern:
- for _, prop := range target.Properties {
- switch prop := prop.(type) {
- case *ast.PropertyShort:
- createIdBinding(prop.Name.Name, int(prop.Name.Idx)-1)
- case *ast.PropertyKeyed:
- c.createBindings(prop.Value, createIdBinding)
- default:
- c.throwSyntaxError(int(target.Idx0()-1), "unsupported property type in ObjectPattern: %T", prop)
- }
- }
- if target.Rest != nil {
- c.createBindings(target.Rest, createIdBinding)
- }
- case *ast.ArrayPattern:
- for _, elt := range target.Elements {
- if elt != nil {
- c.createBindings(elt, createIdBinding)
- }
- }
- if target.Rest != nil {
- c.createBindings(target.Rest, createIdBinding)
- }
- case *ast.AssignExpression:
- c.createBindings(target.Left, createIdBinding)
- default:
- c.throwSyntaxError(int(target.Idx0()-1), "unsupported binding target: %T", target)
- }
- }
- func (c *compiler) createVarBinding(target ast.Expression, inFunc bool) {
- c.createBindings(target, func(name unistring.String, offset int) {
- c.createVarIdBinding(name, offset, inFunc)
- })
- }
- func (c *compiler) createVarBindings(v *ast.VariableDeclaration, inFunc bool) {
- for _, item := range v.List {
- c.createVarBinding(item.Target, inFunc)
- }
- }
- func (c *compiler) createLexicalIdBinding(name unistring.String, isConst bool, offset int) *binding {
- if name == "let" {
- c.throwSyntaxError(offset, "let is disallowed as a lexically bound name")
- }
- if c.scope.strict {
- c.checkIdentifierLName(name, offset)
- c.checkIdentifierName(name, offset)
- }
- b, _ := c.scope.bindNameLexical(name, true, offset)
- if isConst {
- b.isConst, b.isStrict = true, true
- }
- return b
- }
- func (c *compiler) createLexicalIdBindingFuncBody(name unistring.String, isConst bool, offset int, calleeBinding *binding) *binding {
- if name == "let" {
- c.throwSyntaxError(offset, "let is disallowed as a lexically bound name")
- }
- if c.scope.strict {
- c.checkIdentifierLName(name, offset)
- c.checkIdentifierName(name, offset)
- }
- paramScope := c.scope.outer
- parentBinding := paramScope.boundNames[name]
- if parentBinding != nil {
- if parentBinding != calleeBinding && (name != "arguments" || !paramScope.argsNeeded) {
- c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
- }
- }
- b, _ := c.scope.bindNameLexical(name, true, offset)
- if isConst {
- b.isConst, b.isStrict = true, true
- }
- return b
- }
- func (c *compiler) createLexicalBinding(target ast.Expression, isConst bool) {
- c.createBindings(target, func(name unistring.String, offset int) {
- c.createLexicalIdBinding(name, isConst, offset)
- })
- }
- func (c *compiler) createLexicalBindings(lex *ast.LexicalDeclaration) {
- for _, d := range lex.List {
- c.createLexicalBinding(d.Target, lex.Token == token.CONST)
- }
- }
- func (c *compiler) compileLexicalDeclarations(list []ast.Statement, scopeDeclared bool) bool {
- for _, st := range list {
- if lex, ok := st.(*ast.LexicalDeclaration); ok {
- if !scopeDeclared {
- c.newBlockScope()
- scopeDeclared = true
- }
- c.createLexicalBindings(lex)
- } else if cls, ok := st.(*ast.ClassDeclaration); ok {
- if !scopeDeclared {
- c.newBlockScope()
- scopeDeclared = true
- }
- c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1)
- }
- }
- return scopeDeclared
- }
- func (c *compiler) compileLexicalDeclarationsFuncBody(list []ast.Statement, calleeBinding *binding) {
- for _, st := range list {
- if lex, ok := st.(*ast.LexicalDeclaration); ok {
- isConst := lex.Token == token.CONST
- for _, d := range lex.List {
- c.createBindings(d.Target, func(name unistring.String, offset int) {
- c.createLexicalIdBindingFuncBody(name, isConst, offset, calleeBinding)
- })
- }
- } else if cls, ok := st.(*ast.ClassDeclaration); ok {
- c.createLexicalIdBindingFuncBody(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1, calleeBinding)
- }
- }
- }
- func (c *compiler) compileFunction(v *ast.FunctionDeclaration) {
- name := v.Function.Name.Name
- b := c.scope.boundNames[name]
- if b == nil || b.isVar {
- e := &compiledIdentifierExpr{
- name: v.Function.Name.Name,
- }
- e.init(c, v.Function.Idx0())
- e.emitSetter(c.compileFunctionLiteral(v.Function, false), false)
- } else {
- c.compileFunctionLiteral(v.Function, false).emitGetter(true)
- b.emitInitP()
- }
- }
- func (c *compiler) compileStandaloneFunctionDecl(v *ast.FunctionDeclaration) {
- if v.Function.Async {
- c.throwSyntaxError(int(v.Idx0())-1, "Async functions can only be declared at top level or inside a block.")
- }
- if v.Function.Generator {
- c.throwSyntaxError(int(v.Idx0())-1, "Generators can only be declared at top level or inside a block.")
- }
- if c.scope.strict {
- c.throwSyntaxError(int(v.Idx0())-1, "In strict mode code, functions can only be declared at top level or inside a block.")
- }
- 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.")
- }
- func (c *compiler) emit(instructions ...instruction) {
- c.p.code = append(c.p.code, instructions...)
- }
- func (c *compiler) throwSyntaxError(offset int, format string, args ...interface{}) {
- panic(&CompilerSyntaxError{
- CompilerError: CompilerError{
- File: c.p.src,
- Offset: offset,
- Message: fmt.Sprintf(format, args...),
- },
- })
- }
- func (c *compiler) isStrict(list []ast.Statement) *ast.StringLiteral {
- for _, st := range list {
- if st, ok := st.(*ast.ExpressionStatement); ok {
- if e, ok := st.Expression.(*ast.StringLiteral); ok {
- if e.Literal == `"use strict"` || e.Literal == `'use strict'` {
- return e
- }
- } else {
- break
- }
- } else {
- break
- }
- }
- return nil
- }
- func (c *compiler) isStrictStatement(s ast.Statement) *ast.StringLiteral {
- if s, ok := s.(*ast.BlockStatement); ok {
- return c.isStrict(s.List)
- }
- return nil
- }
- func (c *compiler) checkIdentifierName(name unistring.String, offset int) {
- switch name {
- case "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield":
- c.throwSyntaxError(offset, "Unexpected strict mode reserved word")
- }
- }
- func (c *compiler) checkIdentifierLName(name unistring.String, offset int) {
- switch name {
- case "eval", "arguments":
- c.throwSyntaxError(offset, "Assignment to eval or arguments is not allowed in strict mode")
- }
- }
- // Enter a 'dummy' compilation mode. Any code produced after this method is called will be discarded after
- // leaveFunc is called with no additional side effects. This is useful for compiling code inside a
- // constant falsy condition 'if' branch or a loop (i.e 'if (false) { ... } or while (false) { ... }).
- // Such code should not be included in the final compilation result as it's never called, but it must
- // still produce compilation errors if there are any.
- // TODO: make sure variable lookups do not de-optimise parent scopes
- func (c *compiler) enterDummyMode() (leaveFunc func()) {
- savedBlock, savedProgram := c.block, c.p
- if savedBlock != nil {
- c.block = &block{
- typ: savedBlock.typ,
- label: savedBlock.label,
- outer: savedBlock.outer,
- breaking: savedBlock.breaking,
- }
- }
- c.p = &Program{
- src: c.p.src,
- }
- c.newScope()
- return func() {
- c.block, c.p = savedBlock, savedProgram
- c.popScope()
- }
- }
- func (c *compiler) compileStatementDummy(statement ast.Statement) {
- leave := c.enterDummyMode()
- c.compileStatement(statement, false)
- leave()
- }
- func (c *compiler) assert(cond bool, offset int, msg string, args ...interface{}) {
- if !cond {
- c.throwSyntaxError(offset, "Compiler bug: "+msg, args...)
- }
- }
- func privateIdString(desc unistring.String) unistring.String {
- return asciiString("#").Concat(stringValueFromRaw(desc)).string()
- }
- type privateName struct {
- idx int
- isStatic bool
- isMethod bool
- hasGetter, hasSetter bool
- }
- type resolvedPrivateName struct {
- name unistring.String
- idx uint32
- level uint8
- isStatic bool
- isMethod bool
- }
- func (r *resolvedPrivateName) string() unistring.String {
- return privateIdString(r.name)
- }
- type privateEnvRegistry struct {
- fields, methods []unistring.String
- }
- type classScope struct {
- c *compiler
- privateNames map[unistring.String]*privateName
- instanceEnv, staticEnv privateEnvRegistry
- outer *classScope
- }
- func (r *privateEnvRegistry) createPrivateMethodId(name unistring.String) int {
- r.methods = append(r.methods, name)
- return len(r.methods) - 1
- }
- func (r *privateEnvRegistry) createPrivateFieldId(name unistring.String) int {
- r.fields = append(r.fields, name)
- return len(r.fields) - 1
- }
- func (s *classScope) declarePrivateId(name unistring.String, kind ast.PropertyKind, isStatic bool, offset int) {
- pn := s.privateNames[name]
- if pn != nil {
- if pn.isStatic == isStatic {
- switch kind {
- case ast.PropertyKindGet:
- if pn.hasSetter && !pn.hasGetter {
- pn.hasGetter = true
- return
- }
- case ast.PropertyKindSet:
- if pn.hasGetter && !pn.hasSetter {
- pn.hasSetter = true
- return
- }
- }
- }
- s.c.throwSyntaxError(offset, "Identifier '#%s' has already been declared", name)
- panic("unreachable")
- }
- var env *privateEnvRegistry
- if isStatic {
- env = &s.staticEnv
- } else {
- env = &s.instanceEnv
- }
- pn = &privateName{
- isStatic: isStatic,
- hasGetter: kind == ast.PropertyKindGet,
- hasSetter: kind == ast.PropertyKindSet,
- }
- if kind != ast.PropertyKindValue {
- pn.idx = env.createPrivateMethodId(name)
- pn.isMethod = true
- } else {
- pn.idx = env.createPrivateFieldId(name)
- }
- if s.privateNames == nil {
- s.privateNames = make(map[unistring.String]*privateName)
- }
- s.privateNames[name] = pn
- }
- func (s *classScope) getDeclaredPrivateId(name unistring.String) *privateName {
- if n := s.privateNames[name]; n != nil {
- return n
- }
- s.c.assert(false, 0, "getDeclaredPrivateId() for undeclared id")
- panic("unreachable")
- }
- func (c *compiler) resolvePrivateName(name unistring.String, offset int) (*resolvedPrivateName, *privateId) {
- level := 0
- for s := c.classScope; s != nil; s = s.outer {
- if len(s.privateNames) > 0 {
- if pn := s.privateNames[name]; pn != nil {
- return &resolvedPrivateName{
- name: name,
- idx: uint32(pn.idx),
- level: uint8(level),
- isStatic: pn.isStatic,
- isMethod: pn.isMethod,
- }, nil
- }
- level++
- }
- }
- if c.ctxVM != nil {
- for s := c.ctxVM.privEnv; s != nil; s = s.outer {
- if id := s.names[name]; id != nil {
- return nil, id
- }
- }
- }
- c.throwSyntaxError(offset, "Private field '#%s' must be declared in an enclosing class", name)
- panic("unreachable")
- }
|