diff --git a/go.mod b/go.mod index 216ea3e5c38..39ee477ff2b 100644 --- a/go.mod +++ b/go.mod @@ -98,3 +98,5 @@ require ( gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) + +replace github.com/dop251/goja => github.com/mstoykov/goja v0.0.0-20231212144616-08f562ee86d0 diff --git a/go.sum b/go.sum index db2a64846d3..58aeef380fa 100644 --- a/go.sum +++ b/go.sum @@ -44,14 +44,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI= github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM= -github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -124,7 +119,6 @@ github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -146,6 +140,8 @@ github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd h1:AC3N94irbx2kWGA8 github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd/go.mod h1:9vRHVuLCjoFfE3GT06X0spdOAO+Zzo4AMjdIwUHBvAk= github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1 h1:94EkGmhXrVUEal+uLwFUf4fMXPhZpM5tYxuIsxrCCbI= github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1/go.mod h1:vk/d9jpexY2Z9Bb0uB4Ndesss1Sr0Z9ZiGUrg5o9VGk= +github.com/mstoykov/goja v0.0.0-20231212144616-08f562ee86d0 h1:AcJZgDvroNJdSX/Ip5hN0P5xhatMwmJBbLHqn3jqjME= +github.com/mstoykov/goja v0.0.0-20231212144616-08f562ee86d0/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/mstoykov/k6-taskqueue-lib v0.1.0 h1:M3eww1HSOLEN6rIkbNOJHhOVhlqnqkhYj7GTieiMBz4= github.com/mstoykov/k6-taskqueue-lib v0.1.0/go.mod h1:PXdINulapvmzF545Auw++SCD69942FeNvUztaa9dVe4= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= @@ -343,7 +339,6 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/guregu/null.v3 v3.3.0 h1:8j3ggqq+NgKt/O7mbFVUFKUMWN+l1AmT5jQmJ6nPh2c= gopkg.in/guregu/null.v3 v3.3.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= diff --git a/js/bundle_test.go b/js/bundle_test.go index 1c689a19e36..53b69809458 100644 --- a/js/bundle_test.go +++ b/js/bundle_test.go @@ -161,11 +161,6 @@ func TestNewBundle(t *testing.T) { "InvalidCompat", "es1", `export default function() {};`, `invalid compatibility mode "es1". Use: "extended", "base"`, }, - // ES2015 modules are not supported - { - "Modules", "base", `export default function() {};`, - "file:///script.js: Line 2:1 Unexpected reserved word (and 2 more errors)", - }, // BigInt is not supported { "BigInt", "base", @@ -503,7 +498,6 @@ func TestNewBundleFromArchive(t *testing.T) { checkArchive(t, arc, lib.RuntimeOptions{}, "") // default options checkArchive(t, arc, extCompatModeRtOpts, "") - checkArchive(t, arc, baseCompatModeRtOpts, "Unexpected reserved word") }) t.Run("es6_script_explicit", func(t *testing.T) { @@ -514,7 +508,6 @@ func TestNewBundleFromArchive(t *testing.T) { checkArchive(t, arc, lib.RuntimeOptions{}, "") checkArchive(t, arc, extCompatModeRtOpts, "") - checkArchive(t, arc, baseCompatModeRtOpts, "Unexpected reserved word") }) t.Run("es5_script_with_extended", func(t *testing.T) { @@ -553,7 +546,6 @@ func TestNewBundleFromArchive(t *testing.T) { arc.CompatibilityMode = "blah" // intentionally break the archive checkArchive(t, arc, lib.RuntimeOptions{}, "invalid compatibility mode") // fails when it uses the archive one checkArchive(t, arc, extCompatModeRtOpts, "") // works when I force the compat mode - checkArchive(t, arc, baseCompatModeRtOpts, "Unexpected reserved word") // failes because of ES6 }) t.Run("script_options_dont_overwrite_metadata", func(t *testing.T) { diff --git a/vendor/github.com/dop251/goja/ast/node.go b/vendor/github.com/dop251/goja/ast/node.go index 3bec89db493..29d4a10e8f9 100644 --- a/vendor/github.com/dop251/goja/ast/node.go +++ b/vendor/github.com/dop251/goja/ast/node.go @@ -288,6 +288,9 @@ type ( SuperExpression struct { Idx file.Idx } + DynamicImportExpression struct { + Idx file.Idx + } UnaryExpression struct { Operator token.Token @@ -336,6 +339,8 @@ func (*ObjectPattern) _expressionNode() {} func (*ArrayPattern) _expressionNode() {} func (*Binding) _expressionNode() {} +func (*DynamicImportExpression) _expressionNode() {} + func (*PropertyShort) _expressionNode() {} func (*PropertyKeyed) _expressionNode() {} @@ -482,7 +487,71 @@ type ( } FunctionDeclaration struct { - Function *FunctionLiteral + Function *FunctionLiteral + IsDefault bool // TODO figure out how to not have to that + } + + ImportDeclaration struct { + Idx file.Idx + ImportClause *ImportClause + FromClause *FromClause + ModuleSpecifier unistring.String + } + + ImportClause struct { + ImportedDefaultBinding *Identifier + NameSpaceImport *NameSpaceImport + NamedImports *NamedImports + } + + NameSpaceImport struct { + ImportedBinding unistring.String + } + + NamedImports struct { + ImportsList []*ImportSpecifier + } + + ImportSpecifier struct { + IdentifierName unistring.String + Alias unistring.String + } + + ExportDeclaration struct { + Idx file.Idx + Variable *VariableStatement + AssignExpression Expression + LexicalDeclaration *LexicalDeclaration + ClassDeclaration *ClassDeclaration + NamedExports *NamedExports + ExportFromClause *ExportFromClause + FromClause *FromClause + HoistableDeclaration *HoistableDeclaration + IsDefault bool + } + + FromClause struct { + ModuleSpecifier unistring.String + } + ExportFromClause struct { + IsWildcard bool + Alias unistring.String + NamedExports *NamedExports + } + + NamedExports struct { + ExportsList []*ExportSpecifier + } + + ExportSpecifier struct { + IdentifierName unistring.String + Alias unistring.String + } + + HoistableDeclaration struct { + FunctionDeclaration *FunctionDeclaration + // GeneratorDeclaration + // AsyncFunc and AsyncGenerator } ClassDeclaration struct { @@ -517,6 +586,9 @@ func (*LexicalDeclaration) _statementNode() {} func (*FunctionDeclaration) _statementNode() {} func (*ClassDeclaration) _statementNode() {} +func (*ExportDeclaration) _statementNode() {} +func (*ImportDeclaration) _statementNode() {} + // =========== // // Declaration // // =========== // @@ -633,6 +705,10 @@ type Program struct { Body []Statement DeclarationList []*VariableDeclaration + ImportEntries []*ImportDeclaration + ExportEntries []*ExportDeclaration + + HasTLA bool File *file.File } @@ -679,6 +755,8 @@ func (self *UnaryExpression) Idx0() file.Idx { } func (self *MetaProperty) Idx0() file.Idx { return self.Idx } +func (self *DynamicImportExpression) Idx0() file.Idx { return self.Idx } + func (self *BadStatement) Idx0() file.Idx { return self.From } func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace } func (self *BranchStatement) Idx0() file.Idx { return self.Idx } @@ -713,6 +791,9 @@ func (self *PropertyShort) Idx0() file.Idx { return self.Name.Id func (self *PropertyKeyed) Idx0() file.Idx { return self.Key.Idx0() } func (self *ExpressionBody) Idx0() file.Idx { return self.Expression.Idx0() } +func (self *ExportDeclaration) Idx0() file.Idx { return self.Idx } +func (self *ImportDeclaration) Idx0() file.Idx { return self.Idx } + func (self *VariableDeclaration) Idx0() file.Idx { return self.Var } func (self *FieldDefinition) Idx0() file.Idx { return self.Idx } func (self *MethodDefinition) Idx0() file.Idx { return self.Idx } @@ -767,10 +848,13 @@ func (self *UnaryExpression) Idx1() file.Idx { } return self.Operand.Idx1() } + func (self *MetaProperty) Idx1() file.Idx { return self.Property.Idx1() } +func (self *DynamicImportExpression) Idx1() file.Idx { return self.Idx + 6 } + func (self *BadStatement) Idx1() file.Idx { return self.To } func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 } func (self *BranchStatement) Idx1() file.Idx { @@ -841,6 +925,14 @@ func (self *PropertyKeyed) Idx1() file.Idx { return self.Value.Idx1() } func (self *ExpressionBody) Idx1() file.Idx { return self.Expression.Idx1() } +func (self *ExportDeclaration) Idx1() file.Idx { + return self.Idx + file.Idx(len(token.EXPORT.String())) +} + +func (self *ImportDeclaration) Idx1() file.Idx { + return self.Idx + file.Idx(len(token.IMPORT.String())) +} + func (self *VariableDeclaration) Idx1() file.Idx { if len(self.List) > 0 { return self.List[len(self.List)-1].Idx1() diff --git a/vendor/github.com/dop251/goja/builtin_object.go b/vendor/github.com/dop251/goja/builtin_object.go index 6bf1ff80ae9..ce096fdf010 100644 --- a/vendor/github.com/dop251/goja/builtin_object.go +++ b/vendor/github.com/dop251/goja/builtin_object.go @@ -284,23 +284,16 @@ func (r *Runtime) object_freeze(call FunctionCall) Value { obj.self.preventExtensions(true) for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() { - if prop, ok := item.value.(*valueProperty); ok { - prop.configurable = false - if !prop.accessor { - prop.writable = false - } + prop := obj.getOwnProp(item.name) + descr := PropertyDescriptor{ + Configurable: FLAG_FALSE, + } + if prop, ok := prop.(*valueProperty); ok && prop.accessor { + // no-op } else { - prop := obj.getOwnProp(item.name) - descr := PropertyDescriptor{ - Configurable: FLAG_FALSE, - } - if prop, ok := prop.(*valueProperty); ok && prop.accessor { - // no-op - } else { - descr.Writable = FLAG_FALSE - } - obj.defineOwnProperty(item.name, descr, true) + descr.Writable = FLAG_FALSE } + obj.defineOwnProperty(item.name, descr, true) } return obj } else { diff --git a/vendor/github.com/dop251/goja/compiler.go b/vendor/github.com/dop251/goja/compiler.go index 967b0a050a2..71a97839db2 100644 --- a/vendor/github.com/dop251/goja/compiler.go +++ b/vendor/github.com/dop251/goja/compiler.go @@ -2,11 +2,11 @@ package goja import ( "fmt" - "github.com/dop251/goja/token" "sort" "github.com/dop251/goja/ast" "github.com/dop251/goja/file" + "github.com/dop251/goja/token" "github.com/dop251/goja/unistring" ) @@ -29,8 +29,9 @@ const ( maskVar = 1 << 30 maskDeletable = 1 << 29 maskStrict = maskDeletable + maskIndirect = 1 << 28 - maskTyp = maskConst | maskVar | maskDeletable + maskTyp = maskConst | maskVar | maskDeletable | maskIndirect ) type varType byte @@ -73,6 +74,8 @@ type Program struct { funcName unistring.String src *file.File srcMap []srcMapItem + + scriptOrModule interface{} } type compiler struct { @@ -80,6 +83,9 @@ type compiler struct { scope *scope block *block + hostResolveImportedModule HostResolveImportedModuleFunc + module *SourceTextModuleRecord + classScope *classScope enumGetExpr compiledEnumGetExpr @@ -90,10 +96,18 @@ type compiler struct { codeScratchpad []instruction } +func (c *compiler) getScriptOrModule() interface{} { + if c.module != nil { + return c.module + } + return c.p // TODO figure comething better +} + type binding struct { scope *scope name unistring.String accessPoints map[*scope]*[]int + getIndirect func(vm *vm) Value isConst bool isStrict bool isArg bool @@ -133,7 +147,9 @@ func (b *binding) markAccessPoint() { func (b *binding) emitGet() { b.markAccessPoint() - if b.isVar && !b.isArg { + if b.getIndirect != nil { + b.scope.c.emit(loadIndirect(b.getIndirect)) + } else if b.isVar && !b.isArg { b.scope.c.emit(loadStack(0)) } else { b.scope.c.emit(loadStackLex(0)) @@ -142,7 +158,9 @@ func (b *binding) emitGet() { func (b *binding) emitGetAt(pos int) { b.markAccessPointAt(pos) - if b.isVar && !b.isArg { + if b.getIndirect != nil { + b.scope.c.p.code[pos] = loadIndirect(b.getIndirect) + } else if b.isVar && !b.isArg { b.scope.c.p.code[pos] = loadStack(0) } else { b.scope.c.p.code[pos] = loadStackLex(0) @@ -150,8 +168,11 @@ func (b *binding) emitGetAt(pos int) { } func (b *binding) emitGetP() { - if b.isVar && !b.isArg { - // no-op + if b.getIndirect != nil { + b.markAccessPoint() + b.scope.c.emit(loadIndirect(b.getIndirect), pop) + } else if b.isVar && !b.isArg { + // noop } else { // make sure TDZ is checked b.markAccessPoint() @@ -236,7 +257,9 @@ func (b *binding) emitInitPAtScope(scope *scope, pos int) { func (b *binding) emitGetVar(callee bool) { b.markAccessPoint() - if b.isVar && !b.isArg { + if b.getIndirect != nil { + b.scope.c.emit(loadIndirect(b.getIndirect)) + } else 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}) @@ -664,6 +687,18 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) { switch i := (*ap).(type) { case loadStack: *ap = loadStash(idx) + case initIndirect: + *ap = initIndirect{idx: idx, getter: i.getter} + case export: + *ap = export{ + idx: idx, + callback: i.callback, + } + case exportLex: + *ap = exportLex{ + idx: idx, + callback: i.callback, + } case storeStack: *ap = storeStash(idx) case storeStackP: @@ -684,6 +719,8 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) { i.idx = idx case *resolveMixed: i.idx = idx + case loadIndirect: + // do nothing default: s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i) } @@ -859,6 +896,9 @@ func (s *scope) makeNamesMap() map[unistring.String]uint32 { if b.isVar { idx |= maskVar } + if b.getIndirect != nil { + idx |= maskIndirect + } names[b.name] = idx } return names @@ -889,6 +929,186 @@ found: s.bindings = s.bindings[:l] } +func (c *compiler) compileModule(module *SourceTextModuleRecord) { + oldModule := c.module + c.module = module + oldResolve := c.hostResolveImportedModule + c.hostResolveImportedModule = module.hostResolveImportedModule + defer func() { + c.module = oldModule + c.hostResolveImportedModule = oldResolve + }() + in := module.body + c.p.scriptOrModule = module + c.p.src = in.File + + c.newScope() + scope := c.scope + scope.dynamic = true + scope.strict = true + c.newBlockScope() + scope = c.scope + scope.funcType = funcModule + // scope.variable = true + c.emit(&enterFunc{ + funcType: funcModule, + }) + c.block = &block{ + outer: c.block, + typ: blockScope, + + // needResult: true, + } + var enter *enterBlock + c.emit(&enterFuncBody{ + funcType: funcModule, + extensible: true, + adjustStack: true, + }) + for _, in := range module.indirectExportEntries { + v, ambiguous := module.ResolveExport(in.exportName) + if v == nil || ambiguous { + c.compileAmbiguousImport(unistring.NewFromString(in.importName)) + } + } + + for _, in := range module.importEntries { + c.compileImportEntry(in) + } + funcs := c.extractFunctions(in.Body) + c.createFunctionBindings(funcs) + c.compileDeclList(in.DeclarationList, false) + vars := make([]unistring.String, len(scope.bindings)) + for i, b := range scope.bindings { + vars[i] = b.name + } + if len(vars) > 0 { + c.emit(&bindVars{names: vars, deletable: false}) + } + if c.compileLexicalDeclarations(in.Body, true) { + c.block = &block{ + outer: c.block, + typ: blockScope, + needResult: true, + } + enter = &enterBlock{} + c.emit(enter) + } + for _, exp := range in.Body { + if imp, ok := exp.(*ast.ImportDeclaration); ok { + c.compileImportDeclaration(imp) + } + } + for _, entry := range module.localExportEntries { + c.compileLocalExportEntry(entry) + } + for _, entry := range module.indirectExportEntries { + c.compileIndirectExportEntry(entry) + } + + c.compileFunctions(funcs) + + c.emit(&loadModulePromise{moduleCore: module}) + c.emit(await) // this to stop us execute once after we initialize globals + c.emit(pop) + + c.compileStatements(in.Body, true) + c.emit(loadUndef) + c.emit(ret) + if enter != nil { + c.leaveScopeBlock(enter) + c.popScope() + } + + scope.finaliseVarAlloc(0) + m := &newModule{ + moduleCore: module, + newAsyncFunc: newAsyncFunc{ + newFunc: newFunc{ + prg: c.p, + name: unistring.String(in.File.Name()), + source: in.File.Source(), + length: 0, + strict: true, + }, + }, + } + c.p = &Program{ + src: in.File, + scriptOrModule: m, + } + c.emit(_loadUndef{}, m, call(0), &setModulePromise{moduleCore: module}) +} + +func (c *compiler) compileImportEntry(in importEntry) { + importedModule, err := c.hostResolveImportedModule(c.module, in.moduleRequest) + if err != nil { + panic(fmt.Errorf("previously resolved module returned error %w", err)) + } + if in.importName != "*" { + resolution, ambiguous := importedModule.ResolveExport(in.importName) + if resolution == nil || ambiguous { + c.compileAmbiguousImport(unistring.NewFromString(in.importName)) + return + } + } + b, _ := c.scope.bindName(unistring.NewFromString(in.localName)) + b.inStash = true + b.isConst = true +} + +func (c *compiler) compileLocalExportEntry(entry exportEntry) { + name := unistring.NewFromString(entry.localName) + b, ok := c.scope.boundNames[name] + if !ok { + if entry.localName != "default" { + // TODO fix index + c.throwSyntaxError(0, "exporting unknown binding: %q", name) + } + b, _ = c.scope.bindName(name) + } + + b.inStash = true + b.markAccessPoint() + + exportName := unistring.NewFromString(entry.localName) + module := c.module + callback := func(vm *vm, getter func() Value) { + vm.r.modules[module].(*SourceTextModuleInstance).exportGetters[exportName.String()] = getter + } + + if entry.lex || !c.scope.boundNames[exportName].isVar { + c.emit(exportLex{callback: callback}) + } else { + c.emit(export{callback: callback}) + } +} + +func (c *compiler) compileIndirectExportEntry(entry exportEntry) { + otherModule, err := c.hostResolveImportedModule(c.module, entry.moduleRequest) + if err != nil { + panic(fmt.Errorf("previously resolved module returned error %w", err)) + } + if entry.importName == "*" { + return + } + b, ambiguous := otherModule.ResolveExport(entry.importName) + if ambiguous || b == nil { + c.compileAmbiguousImport(unistring.NewFromString(entry.importName)) + return + } + + exportName := unistring.NewFromString(entry.exportName).String() + importName := unistring.NewFromString(b.BindingName).String() + module := c.module + c.emit(exportIndirect{callback: func(vm *vm) { + m := vm.r.modules[module] + m.(*SourceTextModuleInstance).exportGetters[exportName] = func() Value { + return vm.r.modules[b.Module].GetBindingValue(importName) + } + }}) +} + func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) { c.ctxVM = evalVm @@ -984,6 +1204,10 @@ func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) { scope.finaliseVarAlloc(0) } +func (c *compiler) compileAmbiguousImport(name unistring.String) { + c.emit(ambiguousImport(name)) +} + func (c *compiler) compileDeclList(v []*ast.VariableDeclaration, inFunc bool) { for _, value := range v { c.createVarBindings(value, inFunc) @@ -1009,6 +1233,13 @@ func (c *compiler) extractFunctions(list []ast.Statement) (funcs []*ast.Function } else { continue } + case *ast.ExportDeclaration: + if st.HoistableDeclaration == nil { + continue + } + if st.HoistableDeclaration.FunctionDeclaration != nil { + decl = st.HoistableDeclaration.FunctionDeclaration + } default: continue } @@ -1026,6 +1257,9 @@ func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) { for _, decl := range funcs { if !decl.Function.Async && !decl.Function.Generator { s.bindNameLexical(decl.Function.Name.Name, false, int(decl.Function.Name.Idx1())-1) + if decl.IsDefault && decl.Function.Name.Name != "default" { + s.bindNameLexical("default", unique, int(decl.Function.Name.Idx1())-1) + } } else { hasNonStandard = true } @@ -1034,12 +1268,18 @@ func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) { for _, decl := range funcs { if decl.Function.Async || decl.Function.Generator { s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1) + if decl.IsDefault && decl.Function.Name.Name != "default" { + s.bindNameLexical("default", unique, int(decl.Function.Name.Idx1())-1) + } } } } } else { for _, decl := range funcs { s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1) + if decl.IsDefault && decl.Function.Name.Name != "default" { + s.bindNameLexical("default", unique, int(decl.Function.Name.Idx1())-1) + } } } } else { @@ -1190,19 +1430,33 @@ func (c *compiler) createLexicalBindings(lex *ast.LexicalDeclaration) { } func (c *compiler) compileLexicalDeclarations(list []ast.Statement, scopeDeclared bool) bool { + declareScope := func() { + if !scopeDeclared { + c.newBlockScope() + scopeDeclared = true + } + } for _, st := range list { - if lex, ok := st.(*ast.LexicalDeclaration); ok { - if !scopeDeclared { - c.newBlockScope() - scopeDeclared = true - } + switch lex := st.(type) { + case *ast.LexicalDeclaration: + declareScope() c.createLexicalBindings(lex) - } else if cls, ok := st.(*ast.ClassDeclaration); ok { - if !scopeDeclared { - c.newBlockScope() - scopeDeclared = true + case *ast.ClassDeclaration: + declareScope() + c.createLexicalIdBinding(lex.Class.Name.Name, false, int(lex.Class.Name.Idx)-1) + case *ast.ExportDeclaration: + if lex.LexicalDeclaration != nil { + declareScope() + c.createLexicalBindings(lex.LexicalDeclaration) + } else if lex.ClassDeclaration != nil { + declareScope() + if lex.IsDefault { + c.createLexicalIdBinding("default", false, int(lex.Idx0())-1) + } else { + cls := lex.ClassDeclaration + c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1) + } } - c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1) } } return scopeDeclared diff --git a/vendor/github.com/dop251/goja/compiler_expr.go b/vendor/github.com/dop251/goja/compiler_expr.go index b4e39898f22..fa849c9b5db 100644 --- a/vendor/github.com/dop251/goja/compiler_expr.go +++ b/vendor/github.com/dop251/goja/compiler_expr.go @@ -134,6 +134,7 @@ const ( funcClsInit funcCtor funcDerivedCtor + funcModule ) type compiledFunctionLiteral struct { @@ -169,6 +170,9 @@ type compiledNewTarget struct { baseCompiledExpr } +type compiledImportMeta struct { + baseCompiledExpr +} type compiledSequenceExpr struct { baseCompiledExpr sequence []compiledExpr @@ -230,6 +234,9 @@ type compiledOptional struct { baseCompiledExpr expr compiledExpr } +type compiledDynamicImport struct { + baseCompiledExpr +} func (e *defaultDeleteExpr) emitGetter(putOnStack bool) { e.expr.emitGetter(false) @@ -1373,9 +1380,10 @@ func (e *compiledFunctionLiteral) compile() (prg *Program, name unistring.String savedPrg := e.c.p preambleLen := 8 // enter, boxThis, loadStack(0), initThis, createArgs, set, loadCallee, init e.c.p = &Program{ - src: e.c.p.src, - code: e.c.newCode(preambleLen, 16), - srcMap: []srcMapItem{{srcPos: e.offset}}, + src: e.c.p.src, + code: e.c.newCode(preambleLen, 16), + srcMap: []srcMapItem{{srcPos: e.offset}}, + scriptOrModule: e.c.getScriptOrModule(), } e.c.newScope() s := e.c.scope @@ -1983,7 +1991,7 @@ func (e *compiledClassLiteral) emitGetter(putOnStack bool) { DeclarationList: elt.DeclarationList, }, true) f.typ = funcClsInit - //f.lhsName = "" + // f.lhsName = "" f.homeObjOffset = 1 staticElements = append(staticElements, clsElement{ body: f, @@ -2198,9 +2206,10 @@ func (e *compiledClassLiteral) compileFieldsAndStaticBlocks(elements []clsElemen } e.c.p = &Program{ - src: savedPrg.src, - funcName: funcName, - code: e.c.newCode(2, 16), + src: savedPrg.src, + funcName: funcName, + code: e.c.newCode(2, 16), + scriptOrModule: e.c.getScriptOrModule(), } e.c.newScope() @@ -2240,7 +2249,7 @@ func (e *compiledClassLiteral) compileFieldsAndStaticBlocks(elements []clsElemen } } } - //e.c.emit(halt) + // e.c.emit(halt) if s.isDynamic() || thisBinding.useCount() > 0 { if s.isDynamic() || thisBinding.inStash { thisBinding.emitInitAt(1) @@ -2327,6 +2336,12 @@ func (c *compiler) compileArrowFunctionLiteral(v *ast.ArrowFunctionLiteral) *com func (c *compiler) emitLoadThis() { b, eval := c.scope.lookupThis() + + if c.module != nil && c.scope.outer != nil && c.scope.outer.outer == nil { // modules don't have this defined + // TODO maybe just add isTopLevel and rewrite the rest of the code + c.emit(_loadUndef{}) + return + } if b != nil { b.emitGet() } else { @@ -2407,12 +2422,25 @@ func (e *compiledNewTarget) emitGetter(putOnStack bool) { } } +func (e *compiledImportMeta) emitGetter(putOnStack bool) { + if putOnStack { + e.addSrcMap() + e.c.emit(loadImportMeta) + } +} + func (c *compiler) compileMetaProperty(v *ast.MetaProperty) compiledExpr { - if v.Meta.Name == "new" || v.Property.Name != "target" { + if v.Meta.Name == "new" && v.Property.Name == "target" { r := &compiledNewTarget{} r.init(c, v.Idx0()) return r } + if v.Meta.Name == "import" && v.Property.Name == "meta" { + r := &compiledImportMeta{} + r.init(c, v.Idx0()) + return r + } + c.throwSyntaxError(int(v.Idx)-1, "Unsupported meta property: %s.%s", v.Meta.Name, v.Property.Name) return nil } @@ -2799,7 +2827,6 @@ func (e *compiledBinaryExpr) emitGetter(putOnStack bool) { } func (c *compiler) compileBinaryExpression(v *ast.BinaryExpression) compiledExpr { - switch v.Operator { case token.LOGICAL_OR: return c.compileLogicalOr(v.Left, v.Right, v.Idx0()) @@ -3179,11 +3206,16 @@ func (c *compiler) compileCallee(v ast.Expression) compiledExpr { c.throwSyntaxError(int(v.Idx0())-1, "'super' keyword unexpected here") panic("unreachable") } + if imp, ok := v.(*ast.DynamicImportExpression); ok { + r := &compiledDynamicImport{} + r.init(c, imp.Idx) + return r + } return c.compileExpression(v) } func (c *compiler) compileCallExpression(v *ast.CallExpression) compiledExpr { - + // fmt.Printf("%+v %+v %T\n", v, v.Callee, v.Callee) args := make([]compiledExpr, len(v.ArgumentList)) isVariadic := false for i, argExpr := range v.ArgumentList { @@ -3278,8 +3310,6 @@ func (c *compiler) compileBooleanLiteral(v *ast.BooleanLiteral) compiledExpr { } func (c *compiler) compileAssignExpression(v *ast.AssignExpression) compiledExpr { - // log.Printf("compileAssignExpression(): %+v", v) - r := &compiledAssignExpr{ left: c.compileExpression(v.Left), right: c.compileExpression(v.Right), @@ -3579,6 +3609,12 @@ func (e *compiledOptional) emitGetter(putOnStack bool) { } } +func (e *compiledDynamicImport) emitGetter(putOnStack bool) { + if putOnStack { + e.c.emit(dynamicImport) + } +} + func (e *compiledAwaitExpression) emitGetter(putOnStack bool) { e.arg.emitGetter(true) e.c.emit(await) diff --git a/vendor/github.com/dop251/goja/compiler_stmt.go b/vendor/github.com/dop251/goja/compiler_stmt.go index 2d3d83b95e9..e280f254ac0 100644 --- a/vendor/github.com/dop251/goja/compiler_stmt.go +++ b/vendor/github.com/dop251/goja/compiler_stmt.go @@ -8,7 +8,6 @@ import ( ) func (c *compiler) compileStatement(v ast.Statement, needResult bool) { - switch v := v.(type) { case *ast.BlockStatement: c.compileBlockStatement(v, needResult) @@ -52,6 +51,10 @@ func (c *compiler) compileStatement(v ast.Statement, needResult bool) { case *ast.WithStatement: c.compileWithStatement(v, needResult) case *ast.DebuggerStatement: + case *ast.ImportDeclaration: + // this is already done, earlier + case *ast.ExportDeclaration: + c.compileExportDeclaration(v) default: c.assert(false, int(v.Idx0())-1, "Unknown statement type: %T", v) panic("unreachable") @@ -739,7 +742,7 @@ func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) { for b := c.block; b != nil; b = b.outer { switch b.typ { case blockTry: - c.emit(saveResult, leaveTry{}, loadResult) + c.emit(leaveTry{}) case blockLoopEnum: c.emit(enumPopClose) } @@ -780,6 +783,165 @@ func (c *compiler) emitVarAssign(name unistring.String, offset int, init compile } } +func (c *compiler) compileExportDeclaration(expr *ast.ExportDeclaration) { + switch { + case expr.Variable != nil: + c.compileVariableStatement(expr.Variable) + case expr.LexicalDeclaration != nil: + c.compileLexicalDeclaration(expr.LexicalDeclaration) + case expr.ClassDeclaration != nil: + cls := expr.ClassDeclaration + if expr.IsDefault { + c.emitLexicalAssign("default", int(cls.Class.Class)-1, c.compileClassLiteral(cls.Class, false)) + } else { + c.compileClassDeclaration(cls) + } + case expr.HoistableDeclaration != nil: // already handled + case expr.AssignExpression != nil: + assign := expr.AssignExpression + c.compileLexicalDeclaration(&ast.LexicalDeclaration{ + Idx: assign.Idx0(), + Token: token.CONST, + List: []*ast.Binding{ + { + Target: &ast.Identifier{ + Name: unistring.String("default"), + Idx: assign.Idx0(), + }, + Initializer: assign, + }, + }, + }) + case expr.ExportFromClause != nil: + from := expr.ExportFromClause + module, err := c.hostResolveImportedModule(c.module, expr.FromClause.ModuleSpecifier.String()) + if err != nil { + c.throwSyntaxError(int(expr.Idx0()), err.Error()) + } + if from.NamedExports == nil { // star export - nothing to do + return + } + for _, name := range from.NamedExports.ExportsList { + value, ambiguous := module.ResolveExport(name.IdentifierName.String()) + + if ambiguous || value == nil { // also ambiguous + continue // ambiguous import already reported + } + + n := name.Alias + if n.String() == "" { + n = name.IdentifierName + } + localB, _ := c.scope.lookupName(n) + if localB == nil { + c.throwSyntaxError(int(expr.Idx0()), "couldn't lookup %s", n) + } + identifier := name.IdentifierName.String() + localB.getIndirect = func(vm *vm) Value { + return vm.r.modules[module].GetBindingValue(identifier) + } + } + } +} + +func (c *compiler) compileImportDeclaration(expr *ast.ImportDeclaration) { + if expr.FromClause == nil { + return // import "specifier"; + } + module, err := c.hostResolveImportedModule(c.module, expr.FromClause.ModuleSpecifier.String()) + if err != nil { + c.throwSyntaxError(int(expr.Idx0()), err.Error()) + } + if expr.ImportClause != nil { + if namespace := expr.ImportClause.NameSpaceImport; namespace != nil { + idx := expr.Idx + c.emitLexicalAssign( + namespace.ImportedBinding, + int(idx), + c.compileEmitterExpr(func() { + c.emit(importNamespace{ + module: module, + }) + }, idx), + ) + } + if named := expr.ImportClause.NamedImports; named != nil { + for _, name := range named.ImportsList { + value, ambiguous := module.ResolveExport(name.IdentifierName.String()) + + if ambiguous || value == nil { + continue // ambiguous import already reports + } + n := name.Alias + if n.String() == "" { + n = name.IdentifierName + } + if value.BindingName == "*namespace*" { + idx := expr.Idx + c.emitLexicalAssign( + n, + int(idx), + c.compileEmitterExpr(func() { + c.emit(importNamespace{ + module: value.Module, + }) + }, idx), + ) + continue + } + + c.checkIdentifierLName(n, int(expr.Idx)) + localB, _ := c.scope.lookupName(n) + if localB == nil { + c.throwSyntaxError(int(expr.Idx0()), "couldn't lookup %s", n) + } + identifier := unistring.NewFromString(value.BindingName).String() + localB.getIndirect = func(vm *vm) Value { + m := vm.r.modules[value.Module] + return m.GetBindingValue(identifier) + } + localB.markAccessPoint() + c.emit(initIndirect{getter: localB.getIndirect}) + + } + } + + if def := expr.ImportClause.ImportedDefaultBinding; def != nil { + value, ambiguous := module.ResolveExport("default") + + if ambiguous || value == nil { + return // already handled + } + + localB, _ := c.scope.lookupName(def.Name) + if localB == nil { + c.throwSyntaxError(int(expr.Idx0()), "couldn't lookup %s", def.Name) + } + if value.BindingName == "*namespace*" { + idx := expr.Idx + c.emitLexicalAssign( + def.Name, + int(idx), + c.compileEmitterExpr(func() { + c.emit(importNamespace{ + module: value.Module, + }) + }, idx), + ) + } else { + identifier := unistring.NewFromString(value.BindingName).String() + localB.getIndirect = func(vm *vm) Value { + m := vm.r.modules[value.Module] + v := m.GetBindingValue(identifier) + return v + } + localB.markAccessPoint() + c.emit(initIndirect{getter: localB.getIndirect}) + } + } + } +} + func (c *compiler) compileVarBinding(expr *ast.Binding) { switch target := expr.Target.(type) { case *ast.Identifier: diff --git a/vendor/github.com/dop251/goja/modules.go b/vendor/github.com/dop251/goja/modules.go new file mode 100644 index 00000000000..24fd519c57c --- /dev/null +++ b/vendor/github.com/dop251/goja/modules.go @@ -0,0 +1,513 @@ +package goja + +import ( + "errors" + "sort" +) + +type HostResolveImportedModuleFunc func(referencingScriptOrModule interface{}, specifier string) (ModuleRecord, error) + +// TODO most things here probably should be unexported and names should be revised before merged in master +// Record should probably be dropped from everywhere + +// ModuleRecord is the common interface for module record as defined in the EcmaScript specification +type ModuleRecord interface { + GetExportedNames(resolveset ...ModuleRecord) []string + ResolveExport(exportName string, resolveset ...ResolveSetElement) (*ResolvedBinding, bool) + Link() error + Evaluate(*Runtime) *Promise +} + +type CyclicModuleRecordStatus uint8 + +const ( + Unlinked CyclicModuleRecordStatus = iota + Linking + Linked + Evaluating + Evaluating_Async + Evaluated +) + +type CyclicModuleRecord interface { + ModuleRecord + RequestedModules() []string + InitializeEnvironment() error + Instantiate(rt *Runtime) (CyclicModuleInstance, error) +} + +type ( + ModuleInstance interface { + GetBindingValue(string) Value + } + CyclicModuleInstance interface { + ModuleInstance + HasTLA() bool + ExecuteModule(rt *Runtime, res, rej func(interface{})) (CyclicModuleInstance, error) + } +) + +type linkState struct { + status map[ModuleRecord]CyclicModuleRecordStatus + dfsIndex map[ModuleRecord]uint + dfsAncestorIndex map[ModuleRecord]uint +} + +func newLinkState() *linkState { + return &linkState{ + status: make(map[ModuleRecord]CyclicModuleRecordStatus), + dfsIndex: make(map[ModuleRecord]uint), + dfsAncestorIndex: make(map[ModuleRecord]uint), + } +} + +func (c *compiler) CyclicModuleRecordConcreteLink(module ModuleRecord) error { + stack := []CyclicModuleRecord{} + if _, err := c.innerModuleLinking(newLinkState(), module, &stack, 0); err != nil { + return err + } + return nil +} + +func (c *compiler) innerModuleLinking(state *linkState, m ModuleRecord, stack *[]CyclicModuleRecord, index uint) (uint, error) { + var module CyclicModuleRecord + var ok bool + if module, ok = m.(CyclicModuleRecord); !ok { + return index, m.Link() + } + if status := state.status[module]; status == Linking || status == Linked || status == Evaluated { + return index, nil + } else if status != Unlinked { + return 0, errors.New("bad status on link") // TODO fix + } + state.status[module] = Linking + state.dfsIndex[module] = index + state.dfsAncestorIndex[module] = index + index++ + *stack = append(*stack, module) + var err error + var requiredModule ModuleRecord + for _, required := range module.RequestedModules() { + requiredModule, err = c.hostResolveImportedModule(module, required) + if err != nil { + return 0, err + } + index, err = c.innerModuleLinking(state, requiredModule, stack, index) + if err != nil { + return 0, err + } + if requiredC, ok := requiredModule.(CyclicModuleRecord); ok { + if state.status[requiredC] == Linking { + if ancestorIndex := state.dfsAncestorIndex[module]; state.dfsAncestorIndex[requiredC] > ancestorIndex { + state.dfsAncestorIndex[requiredC] = ancestorIndex + } + } + } + } + err = module.InitializeEnvironment() + if err != nil { + return 0, err + } + if state.dfsAncestorIndex[module] == state.dfsIndex[module] { + for i := len(*stack) - 1; i >= 0; i-- { + requiredModule := (*stack)[i] + *stack = (*stack)[:i] + state.status[requiredModule] = Linked + if requiredModule == module { + break + } + } + } + return index, nil +} + +type evaluationState struct { + status map[ModuleInstance]CyclicModuleRecordStatus + dfsIndex map[ModuleInstance]uint + dfsAncestorIndex map[ModuleInstance]uint + pendingAsyncDependancies map[ModuleInstance]uint + cycleRoot map[ModuleInstance]CyclicModuleInstance + asyncEvaluation map[CyclicModuleInstance]uint64 + asyncParentModules map[CyclicModuleInstance][]CyclicModuleInstance + evaluationError map[CyclicModuleInstance]interface{} + topLevelCapability map[CyclicModuleRecord]*promiseCapability + + asyncEvaluationCounter uint64 +} + +func newEvaluationState() *evaluationState { + return &evaluationState{ + status: make(map[ModuleInstance]CyclicModuleRecordStatus), + dfsIndex: make(map[ModuleInstance]uint), + dfsAncestorIndex: make(map[ModuleInstance]uint), + pendingAsyncDependancies: make(map[ModuleInstance]uint), + cycleRoot: make(map[ModuleInstance]CyclicModuleInstance), + asyncEvaluation: make(map[CyclicModuleInstance]uint64), + asyncParentModules: make(map[CyclicModuleInstance][]CyclicModuleInstance), + evaluationError: make(map[CyclicModuleInstance]interface{}), + topLevelCapability: make(map[CyclicModuleRecord]*promiseCapability), + } +} + +// TODO have resolve as part of runtime +func (r *Runtime) CyclicModuleRecordEvaluate(c CyclicModuleRecord, resolve HostResolveImportedModuleFunc) *Promise { + if r.modules == nil { + r.modules = make(map[ModuleRecord]ModuleInstance) + } + stackInstance := []CyclicModuleInstance{} + + if r.evaluationState == nil { + r.evaluationState = newEvaluationState() + } + if cap, ok := r.evaluationState.topLevelCapability[c]; ok { + return cap.promise.Export().(*Promise) + } + capability := r.newPromiseCapability(r.getPromise()) + r.evaluationState.topLevelCapability[c] = capability + state := r.evaluationState + _, err := r.innerModuleEvaluation(state, c, &stackInstance, 0, resolve) + if err != nil { + for _, m := range stackInstance { + state.status[m] = Evaluated + state.evaluationError[m] = err + } + + capability.reject(r.ToValue(err)) + } else { + if state.asyncEvaluation[r.modules[c].(CyclicModuleInstance)] == 0 { + state.topLevelCapability[c].resolve(_undefined) + } + } + if len(r.vm.callStack) == 0 { + r.leave() + } + return state.topLevelCapability[c].promise.Export().(*Promise) +} + +func (r *Runtime) innerModuleEvaluation( + state *evaluationState, + m ModuleRecord, stack *[]CyclicModuleInstance, index uint, + resolve HostResolveImportedModuleFunc, +) (idx uint, err error) { + if len(*stack) > 100000 { + panic("too deep dependancy stack of 100000") + } + var cr CyclicModuleRecord + var ok bool + var c CyclicModuleInstance + if cr, ok = m.(CyclicModuleRecord); !ok { + p := m.Evaluate(r) + if p.state == PromiseStateRejected { + return index, p.Result().Export().(error) + } + r.modules[m] = p.Result().Export().(ModuleInstance) // TODO fix this cast ... somehow + return index, nil + } + if _, ok = r.modules[m]; ok { + return index, nil + } + c, err = cr.Instantiate(r) + if err != nil { + // state.evaluationError[cr] = err + // TODO handle this somehow - maybe just panic + return index, err + } + + r.modules[m] = c + if status := state.status[c]; status == Evaluated { + return index, nil + } else if status == Evaluating || status == Evaluating_Async { + // maybe check evaluation error + return index, nil + } + state.status[c] = Evaluating + state.dfsIndex[c] = index + state.dfsAncestorIndex[c] = index + state.pendingAsyncDependancies[c] = 0 + index++ + + *stack = append(*stack, c) + var requiredModule ModuleRecord + for _, required := range cr.RequestedModules() { + requiredModule, err = resolve(m, required) + if err != nil { + state.evaluationError[c] = err + return index, err + } + index, err = r.innerModuleEvaluation(state, requiredModule, stack, index, resolve) + if err != nil { + return index, err + } + requiredInstance := r.GetModuleInstance(requiredModule) + if requiredC, ok := requiredInstance.(CyclicModuleInstance); ok { + if state.status[requiredC] == Evaluating { + if ancestorIndex := state.dfsAncestorIndex[c]; state.dfsAncestorIndex[requiredC] > ancestorIndex { + state.dfsAncestorIndex[requiredC] = ancestorIndex + } + } else { + requiredC = state.cycleRoot[requiredC] + // check stuff + } + if state.asyncEvaluation[requiredC] != 0 { + state.pendingAsyncDependancies[c]++ + state.asyncParentModules[requiredC] = append(state.asyncParentModules[requiredC], c) + } + } + } + if state.pendingAsyncDependancies[c] > 0 || c.HasTLA() { + state.asyncEvaluationCounter++ + state.asyncEvaluation[c] = state.asyncEvaluationCounter + if state.pendingAsyncDependancies[c] == 0 { + r.executeAsyncModule(state, c) + } + } else { + c, err = c.ExecuteModule(r, nil, nil) + if err != nil { + state.evaluationError[c] = err + return index, err + } + } + + if state.dfsAncestorIndex[c] == state.dfsIndex[c] { + for i := len(*stack) - 1; i >= 0; i-- { + requiredModuleInstance := (*stack)[i] + *stack = (*stack)[:i] + if state.asyncEvaluation[requiredModuleInstance] == 0 { + state.status[requiredModuleInstance] = Evaluated + } else { + state.status[requiredModuleInstance] = Evaluating_Async + } + state.cycleRoot[requiredModuleInstance] = c + if requiredModuleInstance == c { + break + } + } + } + return index, nil +} + +func (r *Runtime) executeAsyncModule(state *evaluationState, c CyclicModuleInstance) { + // implement https://262.ecma-international.org/13.0/#sec-execute-async-module + p, res, rej := r.NewPromise() + r.performPromiseThen(p, r.ToValue(func(call FunctionCall) Value { + r.asyncModuleExecutionFulfilled(state, c) + return nil + }), r.ToValue(func(call FunctionCall) Value { + // we use this signature so that goja doesn't try to infer types and wrap them + err := call.Argument(0) + r.asyncModuleExecutionRejected(state, c, err) + return nil + }), nil) + _, _ = c.ExecuteModule(r, res, rej) +} + +func (r *Runtime) asyncModuleExecutionFulfilled(state *evaluationState, c CyclicModuleInstance) { + if state.status[c] == Evaluated { + return + } + state.asyncEvaluation[c] = 0 + // TODO fix this + for m, i := range r.modules { + if i == c { + if cap := state.topLevelCapability[m.(CyclicModuleRecord)]; cap != nil { + cap.resolve(_undefined) + } + break + } + } + execList := make([]CyclicModuleInstance, 0) + r.gatherAvailableAncestors(state, c, &execList) + sort.Slice(execList, func(i, j int) bool { + return state.asyncEvaluation[execList[i]] < state.asyncEvaluation[execList[j]] + }) + for _, m := range execList { + if state.status[m] == Evaluated { + continue + } + if m.HasTLA() { + r.executeAsyncModule(state, m) + } else { + result, err := m.ExecuteModule(r, nil, nil) + if err != nil { + r.asyncModuleExecutionRejected(state, m, err) + continue + } + state.status[m] = Evaluated + if cap := state.topLevelCapability[r.findModuleRecord(result).(CyclicModuleRecord)]; cap != nil { + // TODO having the module instances going through Values and back is likely not a *great* idea + cap.resolve(_undefined) + } + } + } +} + +func (r *Runtime) gatherAvailableAncestors(state *evaluationState, c CyclicModuleInstance, execList *[]CyclicModuleInstance) { + contains := func(m CyclicModuleInstance) bool { + for _, l := range *execList { + if l == m { + return true + } + } + return false + } + for _, m := range state.asyncParentModules[c] { + if contains(m) || state.evaluationError[m] != nil { + continue + } + state.pendingAsyncDependancies[m]-- + if state.pendingAsyncDependancies[m] == 0 { + *execList = append(*execList, m) + if !m.HasTLA() { + r.gatherAvailableAncestors(state, m, execList) + } + } + } +} + +func (r *Runtime) asyncModuleExecutionRejected(state *evaluationState, c CyclicModuleInstance, ex interface{}) { + if state.status[c] == Evaluated { + return + } + state.evaluationError[c] = ex + state.status[c] = Evaluated + for _, m := range state.asyncParentModules[c] { + r.asyncModuleExecutionRejected(state, m, ex) + } + // TODO handle top level capabiltiy better + if cap := state.topLevelCapability[r.findModuleRecord(c).(CyclicModuleRecord)]; cap != nil { + cap.reject(r.ToValue(ex)) + } +} + +// TODO fix this whole thing +func (r *Runtime) findModuleRecord(i ModuleInstance) ModuleRecord { + for m, mi := range r.modules { + if mi == i { + return m + } + } + panic("this should never happen") +} + +func (r *Runtime) GetActiveScriptOrModule() interface{} { // have some better type + if r.vm.prg != nil && r.vm.prg.scriptOrModule != nil { + return r.vm.prg.scriptOrModule + } + for i := len(r.vm.callStack) - 1; i >= 0; i-- { + prg := r.vm.callStack[i].prg + if prg != nil && prg.scriptOrModule != nil { + return prg.scriptOrModule + } + } + return nil +} + +func (r *Runtime) getImportMetaFor(m ModuleRecord) *Object { + if r.importMetas == nil { + r.importMetas = make(map[ModuleRecord]*Object) + } + if o, ok := r.importMetas[m]; ok { + return o + } + o := r.NewObject() + _ = o.SetPrototype(nil) + + var properties []MetaProperty + if r.getImportMetaProperties != nil { + properties = r.getImportMetaProperties(m) + } + + for _, property := range properties { + o.Set(property.Key, property.Value) + } + + if r.finalizeImportMeta != nil { + r.finalizeImportMeta(o, m) + } + + r.importMetas[m] = o + return o +} + +type MetaProperty struct { + Key string + Value Value +} + +func (r *Runtime) SetGetImportMetaProperties(fn func(ModuleRecord) []MetaProperty) { + r.getImportMetaProperties = fn +} + +func (r *Runtime) SetFinalImportMeta(fn func(*Object, ModuleRecord)) { + r.finalizeImportMeta = fn +} + +// TODO fix signature +type ImportModuleDynamicallyCallback func(referencingScriptOrModule interface{}, specifier Value, promiseCapability interface{}) + +func (r *Runtime) SetImportModuleDynamically(callback ImportModuleDynamicallyCallback) { + r.importModuleDynamically = callback +} + +// TODO figure out whether Result should be an Option thing :shrug: +func (r *Runtime) FinishLoadingImportModule(referrer interface{}, specifier Value, payload interface{}, result ModuleRecord, err interface{}) { + // https://262.ecma-international.org/14.0/#sec-FinishLoadingImportedModule + // if err == nil { + // a. a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then + // i. i. Assert: That Record's [[Module]] is result.[[Value]]. + // b. b. Else, append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]]. + + // } + // 2. 2. If payload is a GraphLoadingState Record, then + // a. a. Perform ContinueModuleLoading(payload, result). + // 3. 3. Else, + // a. a. Perform ContinueDynamicImport(payload, result). + r.continueDynamicImport(payload.(*promiseCapability), result, err) // TODO better type inferance +} + +func (r *Runtime) continueDynamicImport(promiseCapability *promiseCapability, result ModuleRecord, err interface{}) { + // https://262.ecma-international.org/14.0/#sec-ContinueDynamicImport + if err != nil { + promiseCapability.reject(r.ToValue(err)) + return + } + // 2. 2. Let module be moduleCompletion.[[Value]]. + module := result + // 3. 3. Let loadPromise be module.LoadRequestedModules(). + loadPromise := r.promiseResolve(r.getPromise(), _undefined) // TODO fix + + rejectionClosure := r.ToValue(func(call FunctionCall) Value { + promiseCapability.reject(call.Argument(0)) + return nil + }) + linkAndEvaluateClosure := r.ToValue(func(call FunctionCall) Value { + // a. a. Let link be Completion(module.Link()). + err := module.Link() + if err != nil { + if err != nil { + switch x1 := err.(type) { + case *CompilerSyntaxError: + err = &Exception{ + val: r.builtin_new(r.getSyntaxError(), []Value{newStringValue(x1.Error())}), + } + case *CompilerReferenceError: + err = &Exception{ + val: r.newError(r.getReferenceError(), x1.Message), + } // TODO proper message + } + } + promiseCapability.reject(r.ToValue(err)) + return nil + } + evaluationPromise := module.Evaluate(r) + onFullfill := r.ToValue(func(call FunctionCall) Value { + namespace := r.NamespaceObjectFor(module) + promiseCapability.resolve(namespace) + return nil + }) + r.performPromiseThen(evaluationPromise, onFullfill, rejectionClosure, nil) + return nil + }) + + r.performPromiseThen(loadPromise.Export().(*Promise), linkAndEvaluateClosure, rejectionClosure, nil) +} diff --git a/vendor/github.com/dop251/goja/modules_namespace.go b/vendor/github.com/dop251/goja/modules_namespace.go new file mode 100644 index 00000000000..a6dd170a584 --- /dev/null +++ b/vendor/github.com/dop251/goja/modules_namespace.go @@ -0,0 +1,181 @@ +package goja + +import ( + "sort" + + "github.com/dop251/goja/unistring" +) + +type namespaceObject struct { + baseObject + m ModuleRecord + exports map[unistring.String]struct{} + exportsNames []unistring.String +} + +func (r *Runtime) NamespaceObjectFor(m ModuleRecord) *Object { + if r.moduleNamespaces == nil { + r.moduleNamespaces = make(map[ModuleRecord]*namespaceObject) + } + if o, ok := r.moduleNamespaces[m]; ok { + return o.val + } + o := r.createNamespaceObject(m) + r.moduleNamespaces[m] = o + return o.val +} + +func (r *Runtime) createNamespaceObject(m ModuleRecord) *namespaceObject { + o := &Object{runtime: r} + no := &namespaceObject{m: m} + no.val = o + no.extensible = true + no.defineOwnPropertySym(SymToStringTag, PropertyDescriptor{ + Value: newStringValue("Module"), + }, true) + no.extensible = false + o.self = no + no.init() + no.exports = make(map[unistring.String]struct{}) + + for _, exportName := range m.GetExportedNames() { + v, ambiguous := no.m.ResolveExport(exportName) + if ambiguous || v == nil { + continue + } + no.exports[unistring.NewFromString(exportName)] = struct{}{} + no.exportsNames = append(no.exportsNames, unistring.NewFromString(exportName)) + } + return no +} + +func (no *namespaceObject) stringKeys(all bool, accum []Value) []Value { + for name := range no.exports { + if !all { // TODO this seems off + _ = no.getOwnPropStr(name) + } + accum = append(accum, stringValueFromRaw(name)) + } + // TODO optimize this + sort.Slice(accum, func(i, j int) bool { + return accum[i].String() < accum[j].String() + }) + return accum +} + +type namespacePropIter struct { + no *namespaceObject + idx int +} + +func (no *namespaceObject) iterateStringKeys() iterNextFunc { + return (&namespacePropIter{ + no: no, + }).next +} + +func (no *namespaceObject) iterateKeys() iterNextFunc { + return no.iterateStringKeys() +} + +func (i *namespacePropIter) next() (propIterItem, iterNextFunc) { + for i.idx < len(i.no.exportsNames) { + name := i.no.exportsNames[i.idx] + i.idx++ + prop := i.no.getOwnPropStr(name) + if prop != nil { + return propIterItem{name: stringValueFromRaw(name), value: prop}, i.next + } + } + return propIterItem{}, nil +} + +func (no *namespaceObject) getOwnPropStr(name unistring.String) Value { + if _, ok := no.exports[name]; !ok { + return nil + } + v, ambiguous := no.m.ResolveExport(name.String()) + if ambiguous || v == nil { + no.val.runtime.throwReferenceError((name)) + } + if v.BindingName == "*namespace*" { + return &valueProperty{ + value: no.val.runtime.NamespaceObjectFor(v.Module), + writable: true, + configurable: false, + enumerable: true, + } + } + + mi := no.val.runtime.modules[v.Module] + b := mi.GetBindingValue(v.BindingName) + if b == nil { + // TODO figure this out - this is needed as otherwise valueproperty is thought to not have a value + // which isn't really correct in a particular test around isFrozen + b = _null + } + return &valueProperty{ + value: b, + writable: true, + configurable: false, + enumerable: true, + } +} + +func (no *namespaceObject) hasOwnPropertyStr(name unistring.String) bool { + _, ok := no.exports[name] + return ok +} + +func (no *namespaceObject) getStr(name unistring.String, receiver Value) Value { + prop := no.getOwnPropStr(name) + if prop, ok := prop.(*valueProperty); ok { + if receiver == nil { + return prop.get(no.val) + } + return prop.get(receiver) + } + return prop +} + +func (no *namespaceObject) setOwnStr(name unistring.String, val Value, throw bool) bool { + no.val.runtime.typeErrorResult(throw, "Cannot add property %s, object is not extensible", name) + return false +} + +func (no *namespaceObject) deleteStr(name unistring.String, throw bool) bool { + if _, exists := no.exports[name]; exists { + no.val.runtime.typeErrorResult(throw, "Cannot add property %s, object is not extensible", name) + return false + } + return true +} + +func (no *namespaceObject) defineOwnPropertyStr(name unistring.String, desc PropertyDescriptor, throw bool) bool { + returnFalse := func() bool { + if throw { + no.val.runtime.typeErrorResult(throw, "Cannot add property %s, object is not extensible", name) + } + return false + } + if !no.hasOwnPropertyStr(name) { + return returnFalse() + } + if desc.Empty() { + return true + } + if desc.Writable == FLAG_FALSE { + return returnFalse() + } + if desc.Configurable == FLAG_TRUE { + return returnFalse() + } + if desc.Enumerable == FLAG_FALSE { + return returnFalse() + } + if desc.Value != nil && desc.Value != no.getOwnPropStr(name) { + return returnFalse() + } + // TODO more checks + return true +} diff --git a/vendor/github.com/dop251/goja/modules_sourcetext.go b/vendor/github.com/dop251/goja/modules_sourcetext.go new file mode 100644 index 00000000000..f78cdb4bff0 --- /dev/null +++ b/vendor/github.com/dop251/goja/modules_sourcetext.go @@ -0,0 +1,586 @@ +package goja + +import ( + "fmt" + "sort" + "sync" + + "github.com/dop251/goja/ast" + "github.com/dop251/goja/parser" +) + +var ( + _ CyclicModuleRecord = &SourceTextModuleRecord{} + _ CyclicModuleInstance = &SourceTextModuleInstance{} +) + +type SourceTextModuleInstance struct { + moduleRecord *SourceTextModuleRecord + // TODO figure out omething less idiotic + exportGetters map[string]func() Value + pcap *promiseCapability + asyncPromise *Promise +} + +func (s *SourceTextModuleInstance) ExecuteModule(rt *Runtime, res, rej func(interface{})) (CyclicModuleInstance, error) { + promiseP := s.pcap.promise.self.(*Promise) + if len(promiseP.fulfillReactions) == 1 { + ar := promiseP.fulfillReactions[0].asyncRunner + _ = ar.onFulfilled(FunctionCall{Arguments: []Value{_undefined}}) + } + + promise := s.asyncPromise + if !s.HasTLA() { + if res != nil { + panic("goja bug where a not async module was executed as async on") + } + switch s.asyncPromise.state { + case PromiseStateFulfilled: + return s, nil + case PromiseStateRejected: + return nil, rt.vm.exceptionFromValue(promise.result) + case PromiseStatePending: + // TODO !??!? + panic("goja bug where an sync module was not executed synchronously") + default: + panic("Somehow promise from a module execution is in invalid state") + } + } + if res == nil { + panic("goja bug where an async module was not executed as async") + } + rt.performPromiseThen(s.asyncPromise, rt.ToValue(func(call FunctionCall) Value { + // fmt.Println("!!!!res") + res(call.Argument(0)) + return nil + }), rt.ToValue(func(call FunctionCall) Value { + v := call.Argument(0) + rej(rt.vm.exceptionFromValue(v)) + return nil + }), nil) + return nil, nil +} + +func (s *SourceTextModuleInstance) GetBindingValue(name string) Value { + getter, ok := s.exportGetters[name] + if !ok { // let's not panic in case somebody asks for a binding that isn't exported + return nil + } + return getter() +} + +func (s *SourceTextModuleInstance) HasTLA() bool { + return s.moduleRecord.hasTLA // TODO implement when TLA is added +} + +type SourceTextModuleRecord struct { + body *ast.Program + p *Program + // context + // importmeta + hasTLA bool + requestedModules []string + importEntries []importEntry + localExportEntries []exportEntry + indirectExportEntries []exportEntry + starExportEntries []exportEntry + + hostResolveImportedModule HostResolveImportedModuleFunc + + once *sync.Once +} + +type importEntry struct { + moduleRequest string + importName string + localName string + offset int +} + +type exportEntry struct { + exportName string + moduleRequest string + importName string + localName string + + // not standard + lex bool +} + +func importEntriesFromAst(declarations []*ast.ImportDeclaration) ([]importEntry, error) { + var result []importEntry + names := make(map[string]struct{}, len(declarations)) + for _, importDeclarion := range declarations { + importClause := importDeclarion.ImportClause + if importDeclarion.FromClause == nil { + continue // no entry in this case + } + moduleRequest := importDeclarion.FromClause.ModuleSpecifier.String() + if named := importClause.NamedImports; named != nil { + for _, el := range named.ImportsList { + localName := el.Alias.String() + if localName == "" { + localName = el.IdentifierName.String() + } + if _, ok := names[localName]; ok { + return nil, fmt.Errorf("duplicate bounded name %s", localName) + } + names[localName] = struct{}{} + result = append(result, importEntry{ + moduleRequest: moduleRequest, + importName: el.IdentifierName.String(), + localName: localName, + offset: int(importDeclarion.Idx0()), + }) + } + } + if def := importClause.ImportedDefaultBinding; def != nil { + localName := def.Name.String() + if _, ok := names[localName]; ok { + return nil, fmt.Errorf("duplicate bounded name %s", localName) + } + names[localName] = struct{}{} + result = append(result, importEntry{ + moduleRequest: moduleRequest, + importName: "default", + localName: localName, + offset: int(importDeclarion.Idx0()), + }) + } + if namespace := importClause.NameSpaceImport; namespace != nil { + localName := namespace.ImportedBinding.String() + if _, ok := names[localName]; ok { + return nil, fmt.Errorf("duplicate bounded name %s", localName) + } + names[localName] = struct{}{} + result = append(result, importEntry{ + moduleRequest: moduleRequest, + importName: "*", + localName: namespace.ImportedBinding.String(), + offset: int(importDeclarion.Idx0()), + }) + } + } + return result, nil +} + +func exportEntriesFromAst(declarations []*ast.ExportDeclaration) []exportEntry { + var result []exportEntry + for _, exportDeclaration := range declarations { + if exportDeclaration.ExportFromClause != nil { + exportFromClause := exportDeclaration.ExportFromClause + if namedExports := exportFromClause.NamedExports; namedExports != nil { + for _, spec := range namedExports.ExportsList { + result = append(result, exportEntry{ + localName: spec.IdentifierName.String(), + exportName: spec.Alias.String(), + }) + } + } else if exportFromClause.IsWildcard { + if from := exportDeclaration.FromClause; from != nil { + result = append(result, exportEntry{ + exportName: exportFromClause.Alias.String(), + importName: "*", + moduleRequest: from.ModuleSpecifier.String(), + }) + } else { + result = append(result, exportEntry{ + exportName: exportFromClause.Alias.String(), + importName: "*", + }) + } + } else { + panic("wat") + } + } else if variableDeclaration := exportDeclaration.Variable; variableDeclaration != nil { + for _, l := range variableDeclaration.List { + id, ok := l.Target.(*ast.Identifier) + if !ok { + panic("target wasn;t identifier") + } + result = append(result, exportEntry{ + localName: id.Name.String(), + exportName: id.Name.String(), + lex: false, + }) + + } + } else if LexicalDeclaration := exportDeclaration.LexicalDeclaration; LexicalDeclaration != nil { + for _, l := range LexicalDeclaration.List { + + id, ok := l.Target.(*ast.Identifier) + if !ok { + panic("target wasn;t identifier") + } + result = append(result, exportEntry{ + localName: id.Name.String(), + exportName: id.Name.String(), + lex: true, + }) + + } + } else if hoistable := exportDeclaration.HoistableDeclaration; hoistable != nil { + localName := "default" + exportName := "default" + if hoistable.FunctionDeclaration != nil { + if hoistable.FunctionDeclaration.Function.Name != nil { + localName = string(hoistable.FunctionDeclaration.Function.Name.Name.String()) + } + } + if !exportDeclaration.IsDefault { + exportName = localName + } + result = append(result, exportEntry{ + localName: localName, + exportName: exportName, + lex: true, + }) + } else if fromClause := exportDeclaration.FromClause; fromClause != nil { + if namedExports := exportDeclaration.NamedExports; namedExports != nil { + for _, spec := range namedExports.ExportsList { + alias := spec.IdentifierName.String() + if spec.Alias.String() != "" { // TODO fix + alias = spec.Alias.String() + } + result = append(result, exportEntry{ + importName: spec.IdentifierName.String(), + exportName: alias, + moduleRequest: fromClause.ModuleSpecifier.String(), + }) + } + } else { + panic("wat") + } + } else if namedExports := exportDeclaration.NamedExports; namedExports != nil { + for _, spec := range namedExports.ExportsList { + alias := spec.IdentifierName.String() + if spec.Alias.String() != "" { // TODO fix + alias = spec.Alias.String() + } + result = append(result, exportEntry{ + localName: spec.IdentifierName.String(), + exportName: alias, + }) + } + } else if exportDeclaration.AssignExpression != nil { + result = append(result, exportEntry{ + exportName: "default", + localName: "default", + lex: true, + }) + } else if exportDeclaration.ClassDeclaration != nil { + cls := exportDeclaration.ClassDeclaration.Class + if exportDeclaration.IsDefault { + result = append(result, exportEntry{ + exportName: "default", + localName: "default", + lex: true, + }) + } else { + result = append(result, exportEntry{ + exportName: cls.Name.Name.String(), + localName: cls.Name.Name.String(), + lex: true, + }) + } + } else { + panic("wat") + } + } + return result +} + +func requestedModulesFromAst(statements []ast.Statement) []string { + var result []string + for _, st := range statements { + switch imp := st.(type) { + case *ast.ImportDeclaration: + if imp.FromClause != nil { + result = append(result, imp.FromClause.ModuleSpecifier.String()) + } else { + result = append(result, imp.ModuleSpecifier.String()) + } + case *ast.ExportDeclaration: + if imp.FromClause != nil { + result = append(result, imp.FromClause.ModuleSpecifier.String()) + } + } + } + return result +} + +func findImportByLocalName(importEntries []importEntry, name string) (importEntry, bool) { + for _, i := range importEntries { + if i.localName == name { + return i, true + } + } + + return importEntry{}, false +} + +// This should probably be part of Parse +// TODO arguments to this need fixing +func ParseModule(name, sourceText string, resolveModule HostResolveImportedModuleFunc, opts ...parser.Option) (*SourceTextModuleRecord, error) { + // TODO asserts + opts = append(opts, parser.IsModule) + body, err := Parse(name, sourceText, opts...) + _ = body + if err != nil { + return nil, err + } + return ModuleFromAST(body, resolveModule) +} + +func ModuleFromAST(body *ast.Program, resolveModule HostResolveImportedModuleFunc) (*SourceTextModuleRecord, error) { + requestedModules := requestedModulesFromAst(body.Body) + importEntries, err := importEntriesFromAst(body.ImportEntries) + if err != nil { + // TODO create a separate error type + return nil, &CompilerSyntaxError{CompilerError: CompilerError{ + Message: err.Error(), + }} + } + // 6. Let importedBoundNames be ImportedLocalNames(importEntries). + // ^ is skipped as we don't need it. + + var indirectExportEntries []exportEntry + var localExportEntries []exportEntry + var starExportEntries []exportEntry + exportEntries := exportEntriesFromAst(body.ExportEntries) + for _, ee := range exportEntries { + if ee.moduleRequest == "" { // technically nil + if ie, ok := findImportByLocalName(importEntries, ee.localName); !ok { + localExportEntries = append(localExportEntries, ee) + } else { + if ie.importName == "*" { + localExportEntries = append(localExportEntries, ee) + } else { + indirectExportEntries = append(indirectExportEntries, exportEntry{ + moduleRequest: ie.moduleRequest, + importName: ie.importName, + exportName: ee.exportName, + }) + } + } + } else { + if ee.importName == "*" && ee.exportName == "" { + starExportEntries = append(starExportEntries, ee) + } else { + indirectExportEntries = append(indirectExportEntries, ee) + } + } + } + + s := &SourceTextModuleRecord{ + // realm isn't implement + // environment is undefined + // namespace is undefined + hasTLA: body.HasTLA, + requestedModules: requestedModules, + // hostDefined TODO + body: body, + // Context empty + // importMeta empty + importEntries: importEntries, + localExportEntries: localExportEntries, + indirectExportEntries: indirectExportEntries, + starExportEntries: starExportEntries, + + hostResolveImportedModule: resolveModule, + once: &sync.Once{}, + } + + names := s.getExportedNamesWithotStars() // we use this as the other one loops but wee need to early errors here + sort.Strings(names) + for i := 1; i < len(names); i++ { + if names[i] == names[i-1] { + return nil, &CompilerSyntaxError{ + CompilerError: CompilerError{ + Message: fmt.Sprintf("Duplicate export name %s", names[i]), + }, + } + } + // TODO other checks + } + + return s, nil +} + +func (module *SourceTextModuleRecord) getExportedNamesWithotStars() []string { + exportedNames := make([]string, 0, len(module.localExportEntries)+len(module.indirectExportEntries)) + for _, e := range module.localExportEntries { + exportedNames = append(exportedNames, e.exportName) + } + for _, e := range module.indirectExportEntries { + exportedNames = append(exportedNames, e.exportName) + } + return exportedNames +} + +func (module *SourceTextModuleRecord) GetExportedNames(exportStarSet ...ModuleRecord) []string { + for _, el := range exportStarSet { + if el == module { // better check + // TODO assert + return nil + } + } + exportStarSet = append(exportStarSet, module) + var exportedNames []string + for _, e := range module.localExportEntries { + exportedNames = append(exportedNames, e.exportName) + } + for _, e := range module.indirectExportEntries { + exportedNames = append(exportedNames, e.exportName) + } + for _, e := range module.starExportEntries { + requestedModule, err := module.hostResolveImportedModule(module, e.moduleRequest) + if err != nil { + panic(err) + } + starNames := requestedModule.GetExportedNames(exportStarSet...) + + for _, n := range starNames { + if n != "default" { + // TODO check if n i exportedNames and don't include it + exportedNames = append(exportedNames, n) + } + } + } + + return exportedNames +} + +func (module *SourceTextModuleRecord) InitializeEnvironment() (err error) { + module.once.Do(func() { + c := newCompiler() + defer func() { + if x := recover(); x != nil { + switch x1 := x.(type) { + case *CompilerSyntaxError: + err = x1 + default: + panic(x) + } + } + }() + + c.compileModule(module) + module.p = c.p + }) + return +} + +type ResolveSetElement struct { + Module ModuleRecord + ExportName string +} + +type ResolvedBinding struct { + Module ModuleRecord + BindingName string +} + +// GetModuleInstance returns an instance of an already instanciated module. +// If the ModuleRecord was not instanciated at this time it will return nil +func (r *Runtime) GetModuleInstance(m ModuleRecord) ModuleInstance { + return r.modules[m] +} + +func (module *SourceTextModuleRecord) ResolveExport(exportName string, resolveset ...ResolveSetElement) (*ResolvedBinding, bool) { + // TODO this whole algorithm can likely be used for not source module records a well + if exportName == "" { + panic("wat") + } + for _, r := range resolveset { + if r.Module == module && exportName == r.ExportName { // TODO better + return nil, false + } + } + resolveset = append(resolveset, ResolveSetElement{Module: module, ExportName: exportName}) + for _, e := range module.localExportEntries { + if exportName == e.exportName { + // ii. ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }. + return &ResolvedBinding{ + Module: module, + BindingName: e.localName, + }, false + } + } + + for _, e := range module.indirectExportEntries { + if exportName == e.exportName { + importedModule, err := module.hostResolveImportedModule(module, e.moduleRequest) + if err != nil { + panic(err) // TODO return err + } + if e.importName == "*" { + // 2. 2. Return ResolvedBinding Record { [[Module]]: importedModule, [[BindingName]]: "*namespace*" }. + return &ResolvedBinding{ + Module: importedModule, + BindingName: "*namespace*", + }, false + } else { + return importedModule.ResolveExport(e.importName, resolveset...) + } + } + } + if exportName == "default" { + // This actually should've been caught above, but as it didn't it actually makes it s so the `default` export + // doesn't resolve anything that is `export * ...` + return nil, false + } + var starResolution *ResolvedBinding + + for _, e := range module.starExportEntries { + importedModule, err := module.hostResolveImportedModule(module, e.moduleRequest) + if err != nil { + panic(err) // TODO return err + } + resolution, ambiguous := importedModule.ResolveExport(exportName, resolveset...) + if ambiguous { + return nil, true + } + if resolution != nil { + if starResolution == nil { + starResolution = resolution + } else if resolution.Module != starResolution.Module || resolution.BindingName != starResolution.BindingName { + return nil, true + } + } + } + return starResolution, false +} + +func (module *SourceTextModuleRecord) Instantiate(rt *Runtime) (CyclicModuleInstance, error) { + // fmt.Println("Instantiate", module.p.src.Name()) + mi := &SourceTextModuleInstance{ + moduleRecord: module, + exportGetters: make(map[string]func() Value), + pcap: rt.newPromiseCapability(rt.getPromise()), + } + rt.modules[module] = mi + rt.vm.callStack = append(rt.vm.callStack, context{}) + _, ex := rt.RunProgram(module.p) + rt.vm.callStack = rt.vm.callStack[:len(rt.vm.callStack)-1] + if ex != nil { + mi.pcap.reject(rt.ToValue(ex)) + return nil, ex + } + + return mi, nil +} + +func (module *SourceTextModuleRecord) Evaluate(rt *Runtime) *Promise { + return rt.CyclicModuleRecordEvaluate(module, module.hostResolveImportedModule) +} + +func (module *SourceTextModuleRecord) Link() error { + c := newCompiler() + c.hostResolveImportedModule = module.hostResolveImportedModule + return c.CyclicModuleRecordConcreteLink(module) +} + +func (module *SourceTextModuleRecord) RequestedModules() []string { + return module.requestedModules +} diff --git a/vendor/github.com/dop251/goja/object.go b/vendor/github.com/dop251/goja/object.go index 79bd67df429..c281dcf8a3e 100644 --- a/vendor/github.com/dop251/goja/object.go +++ b/vendor/github.com/dop251/goja/object.go @@ -1452,14 +1452,8 @@ func (o *Object) getOwnProp(p Value) Value { } func (o *Object) hasOwnProperty(p Value) bool { - switch p := p.(type) { - case valueInt: - return o.self.hasOwnPropertyIdx(p) - case *Symbol: - return o.self.hasOwnPropertySym(p) - default: - return o.self.hasOwnPropertyStr(p.string()) - } + // https: // 262.ecma-international.org/12.0/#sec-hasownproperty + return o.getOwnProp(p) != nil } func (o *Object) hasProperty(p Value) bool { diff --git a/vendor/github.com/dop251/goja/parser/expression.go b/vendor/github.com/dop251/goja/parser/expression.go index 305bed499fb..a0bcaa43d24 100644 --- a/vendor/github.com/dop251/goja/parser/expression.go +++ b/vendor/github.com/dop251/goja/parser/expression.go @@ -110,6 +110,29 @@ func (self *_parser) parsePrimaryExpression() ast.Expression { return &ast.BadExpression{From: idx, To: self.idx} } +func (self *_parser) parseImportMeta() *ast.MetaProperty { + idx := self.expect(token.IMPORT) + self.expect(token.PERIOD) + if self.literal == "meta" { + return &ast.MetaProperty{ + Meta: &ast.Identifier{ + Name: unistring.String(token.IMPORT.String()), + Idx: idx, + }, + Idx: idx, + Property: self.parseIdentifier(), + } + } + self.errorUnexpectedToken(self.token) + return &ast.MetaProperty{ + Meta: &ast.Identifier{ + Name: unistring.String(token.IMPORT.String()), + Idx: idx, + }, + Idx: idx, + } +} + func (self *_parser) parseSuperProperty() ast.Expression { idx := self.idx self.next() @@ -219,7 +242,6 @@ func (self *_parser) parseParenthesisedExpression() ast.Expression { } func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { - offset := self.chrOffset - 1 // Opening slash already gotten if self.token == token.QUOTIENT_ASSIGN { offset -= 1 // = @@ -370,6 +392,12 @@ func (self *_parser) parseObjectPropertyKey() (string, unistring.String, ast.Exp Value: num, } } + case token.EXPORT, token.IMPORT: + value = &ast.StringLiteral{ + Idx: idx, + Literal: literal, + Value: parsedLiteral, + } case token.PRIVATE_IDENTIFIER: value = &ast.PrivateIdentifier{ Identifier: ast.Identifier{ @@ -540,7 +568,6 @@ func (self *_parser) parseObjectLiteral() *ast.ObjectLiteral { } func (self *_parser) parseArrayLiteral() *ast.ArrayLiteral { - idx0 := self.expect(token.LEFT_BRACKET) var value []ast.Expression for self.token != token.RIGHT_BRACKET && self.token != token.EOF { @@ -632,7 +659,7 @@ func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, i return } -func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { +func (self *_parser) parseCallExpression(left ast.Expression) *ast.CallExpression { argumentList, idx0, idx1 := self.parseArgumentList() return &ast.CallExpression{ Callee: left, @@ -696,6 +723,9 @@ func (self *_parser) parseNewExpression() ast.Expression { if self.token == token.PERIOD { self.next() if self.literal == "target" { + if self.opts.module && self.scope.outer == nil { + self.errorUnexpectedToken(token.IDENTIFIER) // TODO better error + } return &ast.MetaProperty{ Meta: &ast.Identifier{ Name: unistring.String(token.NEW.String()), @@ -711,6 +741,14 @@ func (self *_parser) parseNewExpression() ast.Expression { bad.From = idx return bad } + + if call, ok := callee.(*ast.CallExpression); ok { + if _, ok := call.Callee.(*ast.DynamicImportExpression); ok { + self.error(idx, "You can't use new with import()") + return &ast.BadExpression{From: idx, To: self.idx} + } + } + node := &ast.NewExpression{ New: idx, Callee: callee, @@ -725,10 +763,11 @@ func (self *_parser) parseNewExpression() ast.Expression { } func (self *_parser) parseLeftHandSideExpression() ast.Expression { - var left ast.Expression if self.token == token.NEW { left = self.parseNewExpression() + } else if self.token == token.IMPORT { + left = self.parseImportExpression() } else { left = self.parsePrimaryExpression() } @@ -749,8 +788,32 @@ L: return left } -func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { +func (self *_parser) parseImportExpression() ast.Expression { + idx := self.idx + if self.peek() == token.LEFT_PARENTHESIS { + self.expect(token.IMPORT) + cexp := self.parseCallExpression(&ast.DynamicImportExpression{}) + if len(cexp.ArgumentList) != 1 { + self.error(self.idx, "dynamic import requires exactly one argument") + return &ast.BadExpression{From: idx, To: self.idx} + } + if _, ok := cexp.ArgumentList[0].(*ast.SpreadElement); ok { + self.error(self.idx, "dynamic import can't use spread list") + return &ast.BadExpression{From: idx, To: self.idx} + } + + return cexp + } + if self.opts.module { + return self.parseImportMeta() + } + self.error(self.idx, "import not supported in script") + self.next() + return &ast.BadExpression{From: idx, To: self.idx} +} + +func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { allowIn := self.scope.allowIn self.scope.allowIn = true defer func() { @@ -761,6 +824,8 @@ func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { start := self.idx if self.token == token.NEW { left = self.parseNewExpression() + } else if self.token == token.IMPORT { + left = self.parseImportExpression() } else { left = self.parsePrimaryExpression() } @@ -851,7 +916,6 @@ func (self *_parser) parseUpdateExpression() ast.Expression { } func (self *_parser) parseUnaryExpression() ast.Expression { - switch self.token { case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: fallthrough @@ -878,6 +942,17 @@ func (self *_parser) parseUnaryExpression() ast.Expression { if self.scope.inFuncParams { self.error(idx, "Illegal await-expression in formal parameters of async function") } + scope := self.scope + for scope != nil { + if scope.inFunction { + break + } + if scope.outer == nil { + scope.hasTLA = true + break + } + scope = scope.outer + } return &ast.AwaitExpression{ Await: idx, Argument: self.parseUnaryExpression(), diff --git a/vendor/github.com/dop251/goja/parser/parser.go b/vendor/github.com/dop251/goja/parser/parser.go index 24b380249f5..1994b60f702 100644 --- a/vendor/github.com/dop251/goja/parser/parser.go +++ b/vendor/github.com/dop251/goja/parser/parser.go @@ -52,6 +52,7 @@ const ( ) type options struct { + module bool disableSourceMaps bool sourceMapLoader func(path string) ([]byte, error) } @@ -66,6 +67,10 @@ func WithDisableSourceMaps(opts *options) { opts.disableSourceMaps = true } +func IsModule(opts *options) { + opts.module = true +} + // WithSourceMapLoader is an option to set a custom source map loader. The loader will be given a path or a // URL from the sourceMappingURL. If sourceMappingURL is not absolute it is resolved relatively to the name // of the file being parsed. Any error returned by the loader will fail the parsing. @@ -186,7 +191,6 @@ func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mod // // The parameter list, if any, should be a comma-separated list of identifiers. func ParseFunction(parameterList, body string, options ...Option) (*ast.FunctionLiteral, error) { - src := "(function(" + parameterList + ") {\n" + body + "\n})" parser := _newParser("", src, 1, options...) @@ -212,6 +216,9 @@ func (self *_parser) parse() (*ast.Program, error) { self.openScope() defer self.closeScope() self.next() + if self.opts.module { + self.scope.allowAwait = true + } program := self.parseProgram() if false { self.errors.Sort() diff --git a/vendor/github.com/dop251/goja/parser/scope.go b/vendor/github.com/dop251/goja/parser/scope.go index 5e28ef46756..2853713d5e5 100644 --- a/vendor/github.com/dop251/goja/parser/scope.go +++ b/vendor/github.com/dop251/goja/parser/scope.go @@ -6,17 +6,21 @@ import ( ) type _scope struct { - outer *_scope - allowIn bool - allowLet bool - inIteration bool - inSwitch bool - inFuncParams bool - inFunction bool - inAsync bool - allowAwait bool - allowYield bool - declarationList []*ast.VariableDeclaration + outer *_scope + allowIn bool + allowLet bool + allowImportExport bool + inIteration bool + inSwitch bool + inFuncParams bool + inFunction bool + inAsync bool + allowAwait bool + allowYield bool + declarationList []*ast.VariableDeclaration + importEntries []*ast.ImportDeclaration + exportEntries []*ast.ExportDeclaration + hasTLA bool labels []unistring.String } diff --git a/vendor/github.com/dop251/goja/parser/statement.go b/vendor/github.com/dop251/goja/parser/statement.go index 8ec5cdeb770..d069e9b7412 100644 --- a/vendor/github.com/dop251/goja/parser/statement.go +++ b/vendor/github.com/dop251/goja/parser/statement.go @@ -9,6 +9,7 @@ import ( "github.com/dop251/goja/ast" "github.com/dop251/goja/file" "github.com/dop251/goja/token" + "github.com/dop251/goja/unistring" "github.com/go-sourcemap/sourcemap" ) @@ -36,12 +37,13 @@ func (self *_parser) parseStatementList() (list []ast.Statement) { } func (self *_parser) parseStatement() ast.Statement { - if self.token == token.EOF { self.errorUnexpectedToken(self.token) return &ast.BadStatement{From: self.idx, To: self.idx + 1} } + allowImportExport := self.scope.allowImportExport + self.scope.allowImportExport = false switch self.token { case token.SEMICOLON: return self.parseEmptyStatement() @@ -95,9 +97,47 @@ func (self *_parser) parseStatement() ast.Statement { return self.parseThrowStatement() case token.TRY: return self.parseTryStatement() + case token.EXPORT: + if !self.opts.module { + self.error(self.idx, "export not supported in script") + self.next() + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + if !allowImportExport { + self.error(self.idx, "export only allowed in global scope") + self.next() + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + exp := self.parseExportDeclaration() + if exp != nil { + // TODO this needs to be fixed + self.scope.exportEntries = append(self.scope.exportEntries, exp) + return exp + } + case token.IMPORT: + if self.peek() != token.LEFT_PARENTHESIS { + if !self.opts.module { + self.error(self.idx, "import not supported in script") + self.next() + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + if self.peek() != token.PERIOD { + // this will be parsed as expression + if !allowImportExport { + self.error(self.idx, "import only allowed in global scope") + self.next() + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + imp := self.parseImportDeclaration() + self.scope.importEntries = append(self.scope.importEntries, imp) + + return imp + } + } } expression := self.parseExpression() + // spew.Dump(expression) if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { // LabelledStatement @@ -128,7 +168,6 @@ func (self *_parser) parseStatement() ast.Statement { } func (self *_parser) parseTryStatement() ast.Statement { - node := &ast.TryStatement{ Try: self.expect(token.TRY), Body: self.parseBlockStatement(), @@ -204,7 +243,6 @@ func (self *_parser) parseMaybeAsyncFunction(declaration bool) *ast.FunctionLite } func (self *_parser) parseFunction(declaration, async bool, start file.Idx) *ast.FunctionLiteral { - node := &ast.FunctionLiteral{ Function: start, Async: async, @@ -561,7 +599,6 @@ func (self *_parser) parseWithStatement() ast.Statement { } func (self *_parser) parseCaseStatement() *ast.CaseStatement { - node := &ast.CaseStatement{ Case: self.idx, } @@ -599,7 +636,6 @@ func (self *_parser) parseIterationStatement() ast.Statement { } func (self *_parser) parseForIn(idx file.Idx, into ast.ForInto) *ast.ForInStatement { - // Already have consumed " in" source := self.parseExpression() @@ -614,7 +650,6 @@ func (self *_parser) parseForIn(idx file.Idx, into ast.ForInto) *ast.ForInStatem } func (self *_parser) parseForOf(idx file.Idx, into ast.ForInto) *ast.ForOfStatement { - // Already have consumed " of" source := self.parseAssignmentExpression() @@ -629,7 +664,6 @@ func (self *_parser) parseForOf(idx file.Idx, into ast.ForInto) *ast.ForOfStatem } func (self *_parser) parseFor(idx file.Idx, initializer ast.ForLoopInitializer) *ast.ForStatement { - // Already have consumed " ;" var test, update ast.Expression @@ -780,7 +814,6 @@ func (self *_parser) ensurePatternInit(list []*ast.Binding) { } func (self *_parser) parseVariableStatement() *ast.VariableStatement { - idx := self.expect(token.VAR) list := self.parseVarDeclarationList(idx) @@ -877,6 +910,11 @@ func (self *_parser) parseIfStatement() ast.Statement { func (self *_parser) parseSourceElements() (body []ast.Statement) { for self.token != token.EOF { self.scope.allowLet = true + if self.opts.module { + self.scope.allowImportExport = true + self.scope.allowAwait = true + self.scope.inAsync = true + } body = append(body, self.parseStatement()) } @@ -887,6 +925,9 @@ func (self *_parser) parseProgram() *ast.Program { prg := &ast.Program{ Body: self.parseSourceElements(), DeclarationList: self.scope.declarationList, + ImportEntries: self.scope.importEntries, + ExportEntries: self.scope.exportEntries, + HasTLA: self.scope.hasTLA, File: self.file, } self.file.SetSourceMap(self.parseSourceMap()) @@ -1044,6 +1085,147 @@ illegal: return &ast.BadStatement{From: idx, To: self.idx} } +func (self *_parser) parseExportDeclaration() *ast.ExportDeclaration { + idx := self.expect(token.EXPORT) + + switch self.token { + case token.MULTIPLY: + exportFromClause := self.parseExportFromClause() + fromClause := self.parseFromClause() + self.semicolon() + return &ast.ExportDeclaration{ + Idx: idx, + ExportFromClause: exportFromClause, + FromClause: fromClause, + } + case token.LEFT_BRACE: + namedExports := self.parseNamedExports() + fromClause := self.parseFromClause() + self.semicolon() + return &ast.ExportDeclaration{ + Idx: idx, + NamedExports: namedExports, + FromClause: fromClause, + } + case token.VAR: + return &ast.ExportDeclaration{ + Idx: idx, + Variable: self.parseVariableStatement(), + } + case token.LET, token.CONST: + return &ast.ExportDeclaration{ + Idx: idx, + LexicalDeclaration: self.parseLexicalDeclaration(self.token), + } + case token.ASYNC: + return &ast.ExportDeclaration{ + Idx: idx, + HoistableDeclaration: &ast.HoistableDeclaration{ + FunctionDeclaration: &ast.FunctionDeclaration{ + Function: self.parseMaybeAsyncFunction(true), + }, + }, + } + case token.FUNCTION: + return &ast.ExportDeclaration{ + Idx: idx, + HoistableDeclaration: &ast.HoistableDeclaration{ + FunctionDeclaration: &ast.FunctionDeclaration{ + Function: self.parseFunction(true, false, idx), + }, + }, + } + case token.CLASS: + decl := &ast.ExportDeclaration{ + Idx: idx, + ClassDeclaration: &ast.ClassDeclaration{ + Class: self.parseClass(true), + }, + } + self.insertSemicolon = true + return decl + + case token.DEFAULT: + self.next() + var exp *ast.ExportDeclaration + + switch self.token { + case token.ASYNC: + f := self.parseMaybeAsyncFunction(false) + if f.Name == nil { + f.Name = &ast.Identifier{Name: unistring.String("default"), Idx: f.Idx0()} + } + exp = &ast.ExportDeclaration{ + Idx: idx, + HoistableDeclaration: &ast.HoistableDeclaration{ + FunctionDeclaration: &ast.FunctionDeclaration{ + Function: f, + IsDefault: true, + }, + }, + IsDefault: true, + } + case token.FUNCTION: + f := self.parseFunction(false, false, idx) + if f.Name == nil { + f.Name = &ast.Identifier{Name: unistring.String("default"), Idx: f.Idx0()} + } + exp = &ast.ExportDeclaration{ + Idx: idx, + HoistableDeclaration: &ast.HoistableDeclaration{ + FunctionDeclaration: &ast.FunctionDeclaration{ + Function: f, + IsDefault: true, + }, + }, + IsDefault: true, + } + case token.CLASS: + decl := &ast.ExportDeclaration{ + Idx: idx, + ClassDeclaration: &ast.ClassDeclaration{ + Class: self.parseClass(false), + }, + IsDefault: true, + } + self.insertSemicolon = true + return decl + + default: + exp = &ast.ExportDeclaration{ + Idx: idx, + AssignExpression: self.parseAssignmentExpression(), + IsDefault: true, + } + self.semicolon() + } + return exp + default: + namedExports := self.parseNamedExports() + self.semicolon() + return &ast.ExportDeclaration{ + Idx: idx, + NamedExports: namedExports, + } + } +} + +func (self *_parser) parseImportDeclaration() *ast.ImportDeclaration { + idx := self.expect(token.IMPORT) + + if self.token == token.STRING { + moduleSpecifier := self.parseModuleSpecifier() + self.semicolon() + return &ast.ImportDeclaration{Idx: idx, ModuleSpecifier: moduleSpecifier} + } + + return &ast.ImportDeclaration{ + ImportClause: self.parseImportClause(), + FromClause: self.parseFromClause(), + Idx: idx, + } +} + // Find the next statement after an error (recover) func (self *_parser) nextStatement() { for { @@ -1076,3 +1258,198 @@ func (self *_parser) nextStatement() { self.next() } } + +func (self *_parser) parseFromClause() *ast.FromClause { + if self.literal == "from" { + self.next() + return &ast.FromClause{ + ModuleSpecifier: self.parseModuleSpecifier(), + } + } + return nil +} + +func (self *_parser) parseExportFromClause() *ast.ExportFromClause { + if self.token == token.MULTIPLY { + self.next() + + if self.token != token.IDENTIFIER { + return &ast.ExportFromClause{IsWildcard: true} + } + + if self.literal == "as" { + self.next() + alias := self.parseIdentifier() + return &ast.ExportFromClause{ + IsWildcard: true, + Alias: alias.Name, + } + } + + return &ast.ExportFromClause{ + IsWildcard: true, + } + } + + return &ast.ExportFromClause{ + IsWildcard: false, + NamedExports: self.parseNamedExports(), + } +} + +func (self *_parser) parseNamedExports() *ast.NamedExports { + _ = self.expect(token.LEFT_BRACE) + + exportsList := self.parseExportsList() + + self.expect(token.RIGHT_BRACE) + + return &ast.NamedExports{ + ExportsList: exportsList, + } +} + +func (self *_parser) parseExportsList() (exportsList []*ast.ExportSpecifier) { + if self.token == token.RIGHT_BRACE { + return + } + + for self.token != token.RIGHT_BRACE { + exportsList = append(exportsList, self.parseExportSpecifier()) + if self.token != token.COMMA { + break + } + + self.next() + } + + return +} + +func (self *_parser) parseExportSpecifier() *ast.ExportSpecifier { + identifier := self.parseIdentifier() + + if self.token != token.IDENTIFIER { + return &ast.ExportSpecifier{IdentifierName: identifier.Name} + } + + if self.literal != "as" { + self.error(self.idx, "Expected 'as' keyword, found '%s' instead", self.literal) + } + self.next() + + alias := self.parseIdentifier() + + return &ast.ExportSpecifier{ + IdentifierName: identifier.Name, + Alias: alias.Name, + } +} + +func (self *_parser) parseImportClause() *ast.ImportClause { + switch self.token { + case token.LEFT_BRACE: + return &ast.ImportClause{NamedImports: self.parseNamedImports()} + case token.MULTIPLY: + return &ast.ImportClause{NameSpaceImport: self.parseNameSpaceImport()} + case token.IDENTIFIER: + literal, _, _, _ := self.scanIdentifier() + if literal == "*" { + return &ast.ImportClause{NameSpaceImport: self.parseNameSpaceImport()} + } + + importClause := ast.ImportClause{ + ImportedDefaultBinding: self.parseImportedDefaultBinding(), + } + + if self.token == token.COMMA { + self.expect(token.COMMA) + + if self.token == token.MULTIPLY { + importClause.NameSpaceImport = self.parseNameSpaceImport() + } else if self.token == token.RIGHT_BRACE { + self.next() + return &importClause + } else { + importClause.NamedImports = self.parseNamedImports() + } + } + + return &importClause + } + + return nil // unreachable +} + +func (self *_parser) parseModuleSpecifier() unistring.String { + expression := self.parsePrimaryExpression() + stringLiteral, ok := expression.(*ast.StringLiteral) + if !ok { + self.error(self.idx, "expected module specifier") + return "" + } + + return stringLiteral.Value +} + +func (self *_parser) parseImportedDefaultBinding() *ast.Identifier { + return self.parseImportedBinding() +} + +func (self *_parser) parseNameSpaceImport() *ast.NameSpaceImport { + self.expect(token.MULTIPLY) + if self.literal != "as" { + self.error(self.idx, "Expected 'as' keyword, found '%s' instead", self.literal) + } + self.next() + + return &ast.NameSpaceImport{ImportedBinding: self.parseImportedBinding().Name} +} + +func (self *_parser) parseNamedImports() *ast.NamedImports { + _ = self.expect(token.LEFT_BRACE) + + importsList := self.parseImportsList() + + self.expect(token.RIGHT_BRACE) + + return &ast.NamedImports{ + ImportsList: importsList, + } +} + +func (self *_parser) parseImportsList() (importsList []*ast.ImportSpecifier) { + for self.token != token.RIGHT_BRACE { + importsList = append(importsList, self.parseImportSpecifier()) + if self.token != token.COMMA { + break + } + + self.next() + } + + return +} + +func (self *_parser) parseImportSpecifier() *ast.ImportSpecifier { + importSpecifier := &ast.ImportSpecifier{ + IdentifierName: self.parseImportedBinding().Name, + } + + if self.token == token.IDENTIFIER && self.literal == "as" { + self.next() // skip "as" + importSpecifier.Alias = self.parseIdentifier().Name + } + + return importSpecifier +} + +func (self *_parser) parseImportedBinding() *ast.Identifier { + return self.parseBindingIdentifier() +} + +func (self *_parser) parseBindingIdentifier() *ast.Identifier { + // FIXME: Ecma 262 defines yield and await as permitted binding identifier, + // but goja does not support async/await nor generators yet? + return self.parseIdentifier() +} diff --git a/vendor/github.com/dop251/goja/runtime.go b/vendor/github.com/dop251/goja/runtime.go index 0139ef10db1..331f8347b16 100644 --- a/vendor/github.com/dop251/goja/runtime.go +++ b/vendor/github.com/dop251/goja/runtime.go @@ -192,6 +192,15 @@ type Runtime struct { hash *maphash.Hash idSeq uint64 + modules map[ModuleRecord]ModuleInstance + moduleNamespaces map[ModuleRecord]*namespaceObject + importMetas map[ModuleRecord]*Object + + getImportMetaProperties func(ModuleRecord) []MetaProperty + finalizeImportMeta func(*Object, ModuleRecord) + importModuleDynamically ImportModuleDynamicallyCallback + evaluationState *evaluationState + jobQueue []func() promiseRejectionTracker PromiseRejectionTracker diff --git a/vendor/github.com/dop251/goja/token/token_const.go b/vendor/github.com/dop251/goja/token/token_const.go index 25914273463..819f1ca2337 100644 --- a/vendor/github.com/dop251/goja/token/token_const.go +++ b/vendor/github.com/dop251/goja/token/token_const.go @@ -122,6 +122,9 @@ const ( DEBUGGER INSTANCEOF + // ES6 Modules + EXPORT + IMPORT ESCAPED_RESERVED_WORD // Non-reserved keywords below @@ -235,6 +238,8 @@ var token2string = [...]string{ CONTINUE: "continue", DEBUGGER: "debugger", INSTANCEOF: "instanceof", + EXPORT: "export", + IMPORT: "import", } var keywordTable = map[string]_keyword{ @@ -329,16 +334,14 @@ var keywordTable = map[string]_keyword{ token: KEYWORD, futureKeyword: true, }, - "export": { - token: KEYWORD, - futureKeyword: true, + "import": { + token: IMPORT, }, "extends": { token: EXTENDS, }, - "import": { - token: KEYWORD, - futureKeyword: true, + "export": { + token: EXPORT, }, "super": { token: SUPER, diff --git a/vendor/github.com/dop251/goja/vm.go b/vendor/github.com/dop251/goja/vm.go index 29fffbebebf..00ef5a096bb 100644 --- a/vendor/github.com/dop251/goja/vm.go +++ b/vendor/github.com/dop251/goja/vm.go @@ -3,6 +3,7 @@ package goja import ( "fmt" "math" + "reflect" "strconv" "strings" "sync" @@ -27,6 +28,7 @@ type stash struct { obj *Object outer *stash + vm *vm // If this is a top-level function stash, sets the type of the function. If set, dynamic var declarations // created by direct eval go here. @@ -456,6 +458,10 @@ func (s *stash) getByName(name unistring.String) (v Value, exists bool) { } else { v = _undefined } + } else if idx&maskIndirect != 0 { + var f func(*vm) Value + _ = s.vm.r.ExportTo(v, &f) + v = f(s.vm) } return v, true } @@ -542,6 +548,7 @@ func (s *stash) deleteBinding(name unistring.String) { func (vm *vm) newStash() { vm.stash = &stash{ outer: vm.stash, + vm: vm, // TODO fix } vm.stashAllocs++ } @@ -755,8 +762,7 @@ func (vm *vm) handleThrow(arg interface{}) *Exception { } if int(tf.callStackLen) < len(vm.callStack) { ctx := &vm.callStack[tf.callStackLen] - vm.prg, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args = - ctx.prg, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args + vm.prg, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args = ctx.prg, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args vm.callStack = vm.callStack[:tf.callStackLen] } vm.sp = int(tf.sp) @@ -851,8 +857,7 @@ func (vm *vm) peek() Value { } func (vm *vm) saveCtx(ctx *context) { - ctx.prg, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args = - vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args + ctx.prg, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args = vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args } func (vm *vm) pushCtx() { @@ -867,8 +872,7 @@ func (vm *vm) pushCtx() { } func (vm *vm) restoreCtx(ctx *context) { - vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args = - ctx.prg, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args + vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args = ctx.prg, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args } func (vm *vm) popCtx() { @@ -932,15 +936,6 @@ func (_saveResult) exec(vm *vm) { vm.pc++ } -type _loadResult struct{} - -var loadResult _loadResult - -func (_loadResult) exec(vm *vm) { - vm.push(vm.result) - vm.pc++ -} - type _clearResult struct{} var clearResult _clearResult @@ -1159,6 +1154,66 @@ func (s initStack1P) exec(vm *vm) { vm.sp-- } +type importNamespace struct { + module ModuleRecord +} + +func (i importNamespace) exec(vm *vm) { + vm.push(vm.r.NamespaceObjectFor(i.module)) + vm.pc++ +} + +type export struct { + idx uint32 + callback func(*vm, func() Value) +} + +func (e export) exec(vm *vm) { + // from loadStash + level := int(e.idx >> 24) + idx := uint32(e.idx & 0x00FFFFFF) + stash := vm.stash + for i := 0; i < level; i++ { + stash = stash.outer + } + e.callback(vm, func() Value { + return stash.getByIdx(idx) + }) + vm.pc++ +} + +type exportLex struct { + idx uint32 + callback func(*vm, func() Value) +} + +func (e exportLex) exec(vm *vm) { + // from loadStashLex + level := int(e.idx >> 24) + idx := uint32(e.idx & 0x00FFFFFF) + stash := vm.stash + for i := 0; i < level; i++ { + stash = stash.outer + } + e.callback(vm, func() Value { + v := stash.getByIdx(idx) + if v == nil { + panic(errAccessBeforeInit) + } + return v + }) + vm.pc++ +} + +type exportIndirect struct { + callback func(*vm) +} + +func (e exportIndirect) exec(vm *vm) { + e.callback(vm) + vm.pc++ +} + type storeStackP int func (s storeStackP) exec(vm *vm) { @@ -2618,6 +2673,22 @@ func (s initStash) exec(vm *vm) { vm.initLocal(int(s)) } +type initIndirect struct { + idx uint32 + getter func(vm *vm) Value +} + +func (s initIndirect) exec(vm *vm) { + level := int(s.idx) >> 24 + idx := uint32(s.idx & 0x00FFFFFF) + stash := vm.stash + for i := 0; i < level; i++ { + stash = stash.outer + } + stash.initByIdx(idx, vm.r.ToValue(s.getter)) + vm.pc++ +} + type initStashP uint32 func (s initStashP) exec(vm *vm) { @@ -2766,6 +2837,15 @@ func (s setGlobalStrict) exec(vm *vm) { vm.pc++ } +// Load a var indirectly from another module +type loadIndirect func(vm *vm) Value + +func (g loadIndirect) exec(vm *vm) { + v := nilSafe(g(vm)) + vm.push(v) + vm.pc++ +} + // Load a var from stash type loadStash uint32 @@ -3372,11 +3452,15 @@ func (numargs call) exec(vm *vm) { n := int(numargs) v := vm.stack[vm.sp-n-1] // callee obj := vm.toCallee(v) + obj.self.vmCall(vm, n) } func (vm *vm) clearStack() { sp := vm.sp + if sp > len(vm.stack) { + time.Sleep(time.Second) + } stackTail := vm.stack[sp:] for i := range stackTail { stackTail[i] = nil @@ -3721,6 +3805,41 @@ func (n *newAsyncFunc) exec(vm *vm) { vm.pc++ } +type loadModulePromise struct { + moduleCore ModuleRecord +} + +func (n *loadModulePromise) exec(vm *vm) { + mi := vm.r.modules[n.moduleCore].(*SourceTextModuleInstance) + vm.push(mi.pcap.promise) + vm.pc++ +} + +type newModule struct { + newAsyncFunc + moduleCore ModuleRecord +} + +func (n *newModule) exec(vm *vm) { + obj := vm.r.newAsyncFunc(n.name, n.length, n.strict) + obj.prg = n.prg + obj.stash = vm.stash + obj.privEnv = vm.privEnv + obj.src = n.source + vm.push(obj.val) + vm.pc++ +} + +type setModulePromise struct { + moduleCore ModuleRecord +} + +func (n *setModulePromise) exec(vm *vm) { + mi := vm.r.modules[n.moduleCore].(*SourceTextModuleInstance) + mi.asyncPromise = vm.pop().Export().(*Promise) + vm.pc++ +} + type newGeneratorFunc struct { newFunc } @@ -3789,7 +3908,7 @@ func getFuncObject(v Value) *Object { } return o } - if v == _undefined { + if v == _undefined || v == _null { return nil } panic(typeError("Value is not an Object")) @@ -3827,6 +3946,12 @@ func (n *newArrowFunc) _exec(vm *vm, obj *arrowFuncObject) { vm.pc++ } +type ambiguousImport unistring.String + +func (a ambiguousImport) exec(vm *vm) { + panic(vm.r.newError(vm.r.getSyntaxError(), "Ambiguous import for name %s", a)) +} + func (n *newArrowFunc) exec(vm *vm) { n._exec(vm, vm.r.newArrowFunc(n.name, n.length, n.strict)) } @@ -4208,7 +4333,6 @@ end: return valueTrue } return valueFalse - } type _op_lt struct{} @@ -4446,20 +4570,28 @@ var throw _throw func (_throw) exec(vm *vm) { v := vm.stack[vm.sp-1] - ex := &Exception{ - val: v, - } - + var ex *Exception if o, ok := v.(*Object); ok { if e, ok := o.self.(*errorObject); ok { if len(e.stack) > 0 { - ex.stack = e.stack + frame0 := e.stack[0] + // If the Error was created immediately before throwing it (i.e. 'throw new Error(....)') + // avoid capturing the stack again by the reusing the stack from the Error. + // These stacks would be almost identical and the difference doesn't matter for debugging. + if frame0.prg == vm.prg && vm.pc-frame0.pc == 1 { + ex = &Exception{ + val: v, + stack: e.stack, + } + } } } } - - if ex.stack == nil { - ex.stack = vm.captureStack(make([]StackFrame, 0, len(vm.callStack)+1), 0) + if ex == nil { + ex = &Exception{ + val: v, + stack: vm.captureStack(make([]StackFrame, 0, len(vm.callStack)+1), 0), + } } if ex = vm.handleThrow(ex); ex != nil { @@ -4537,6 +4669,65 @@ func (_loadNewTarget) exec(vm *vm) { vm.pc++ } +type _loadImportMeta struct{} + +var loadImportMeta _loadImportMeta + +func (_loadImportMeta) exec(vm *vm) { + // https://262.ecma-international.org/13.0/#sec-meta-properties-runtime-semantics-evaluation + t := vm.r.GetActiveScriptOrModule() + m := t.(ModuleRecord) // There should be now way for this to have compiled + vm.push(vm.r.getImportMetaFor(m)) + vm.pc++ +} + +type _loadDynamicImport struct{} + +var dynamicImport _loadDynamicImport + +func (_loadDynamicImport) exec(vm *vm) { + // https://262.ecma-international.org/13.0/#sec-import-call-runtime-semantics-evaluation + vm.push(vm.r.ToValue(func(specifier Value) Value { // TODO remove this function + t := vm.r.GetActiveScriptOrModule() + + pcap := vm.r.newPromiseCapability(vm.r.getPromise()) + var specifierStr String + err := vm.r.try(func() { + specifierStr = specifier.toString() + }) + if err != nil { + if ex, ok := err.(*Exception); ok { + pcap.reject(vm.r.ToValue(ex.val)) + } else { + pcap.reject(vm.r.ToValue(err)) + } + return pcap.promise + } + if vm.r.importModuleDynamically == nil { + pcap.reject(asciiString("dynamic modules not enabled in the host program")) + } else { + pcapInput := vm.r.newPromiseCapability(vm.r.getPromise()) + onFullfill := vm.r.ToValue(func(call FunctionCall) Value { + pcap.resolve(call.Argument(0)) + return nil + }) + rejectionClosure := vm.r.ToValue(func(call FunctionCall) Value { + v := call.Argument(0) + if v.ExportType() == reflect.TypeOf(&Exception{}) { + v = v.Export().(*Exception).Value() + } + + pcap.reject(v) + return nil + }) + vm.r.performPromiseThen(pcapInput.promise.Export().(*Promise), onFullfill, rejectionClosure, nil) + vm.r.importModuleDynamically(t, specifierStr, pcapInput) + } + return pcap.promise + })) + vm.pc++ +} + type _typeof struct{} var typeof _typeof diff --git a/vendor/modules.txt b/vendor/modules.txt index d9c212b45e2..db31341ce3f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -104,7 +104,7 @@ github.com/dgryski/go-rendezvous ## explicit; go 1.13 github.com/dlclark/regexp2 github.com/dlclark/regexp2/syntax -# github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 +# github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 => github.com/mstoykov/goja v0.0.0-20231212144616-08f562ee86d0 ## explicit; go 1.16 github.com/dop251/goja github.com/dop251/goja/ast @@ -550,3 +550,4 @@ gopkg.in/guregu/null.v3 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 +# github.com/dop251/goja => github.com/mstoykov/goja v0.0.0-20231212144616-08f562ee86d0