123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- package goja
- import (
- "errors"
- "fmt"
- "gopkg.in/yaml.v2"
- "io/ioutil"
- "os"
- "path"
- "strings"
- "sync"
- "testing"
- )
- const (
- tc39BASE = "testdata/test262"
- )
- var (
- invalidFormatError = errors.New("Invalid file format")
- )
- var (
- skipList = map[string]bool{
- "test/language/literals/regexp/S7.8.5_A1.1_T2.js": true, // UTF-16
- "test/language/literals/regexp/S7.8.5_A1.4_T2.js": true, // UTF-16
- "test/language/literals/regexp/S7.8.5_A2.1_T2.js": true, // UTF-16
- "test/language/literals/regexp/S7.8.5_A2.4_T2.js": true, // UTF-16
- "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js": true, // timezone
- "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js": true, // timezone
- "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
- "test/annexB/built-ins/escape/escape-above-astral.js": true, // \u{xxxxx}
- // utf-16
- "test/built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-string-wrapper.js": true,
- // cross-realm
- "test/built-ins/Symbol/unscopables/cross-realm.js": true,
- "test/built-ins/Symbol/toStringTag/cross-realm.js": true,
- "test/built-ins/Symbol/toPrimitive/cross-realm.js": true,
- "test/built-ins/Symbol/split/cross-realm.js": true,
- "test/built-ins/Symbol/species/cross-realm.js": true,
- "test/built-ins/Symbol/search/cross-realm.js": true,
- "test/built-ins/Symbol/replace/cross-realm.js": true,
- "test/built-ins/Symbol/match/cross-realm.js": true,
- "test/built-ins/Symbol/keyFor/cross-realm.js": true,
- "test/built-ins/Symbol/iterator/cross-realm.js": true,
- "test/built-ins/Symbol/isConcatSpreadable/cross-realm.js": true,
- "test/built-ins/Symbol/hasInstance/cross-realm.js": true,
- "test/built-ins/Symbol/for/cross-realm.js": true,
- "test/built-ins/WeakSet/proto-from-ctor-realm.js": true,
- "test/built-ins/WeakMap/proto-from-ctor-realm.js": true,
- "test/built-ins/Map/proto-from-ctor-realm.js": true,
- "test/built-ins/Set/proto-from-ctor-realm.js": true,
- "test/built-ins/Object/proto-from-ctor.js": true,
- "test/built-ins/Array/from/proto-from-ctor-realm.js": true,
- "test/built-ins/Array/of/proto-from-ctor-realm.js": true,
- "test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-non-array.js": true,
- "test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-array.js": true,
- "test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-non-array.js": true,
- "test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js": true,
- "test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-non-array.js": true,
- "test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-array.js": true,
- "test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-non-array.js": true,
- "test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-array.js": true,
- "test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-non-array.js": true,
- "test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-array.js": true,
- "test/built-ins/Proxy/construct/arguments-realm.js": true,
- "test/built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/getPrototypeOf/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/set/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/getOwnPropertyDescriptor/result-type-is-not-object-nor-undefined-realm.js": true,
- "test/built-ins/Proxy/get/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/preventExtensions/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/defineProperty/null-handler-realm.js": true,
- "test/built-ins/Proxy/ownKeys/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/ownKeys/return-not-list-object-throws-realm.js": true,
- "test/built-ins/Proxy/deleteProperty/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/isExtensible/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/defineProperty/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-undefined-target-is-not-extensible-realm.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-undefined-not-configurable-descriptor-realm.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-realm.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-not-configurable-target-realm.js": true,
- "test/built-ins/Proxy/defineProperty/targetdesc-configurable-desc-not-configurable-realm.js": true,
- "test/built-ins/Proxy/has/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/defineProperty/desc-realm.js": true,
- "test/built-ins/Proxy/apply/trap-is-not-callable-realm.js": true,
- "test/built-ins/Proxy/apply/arguments-realm.js": true,
- "test/built-ins/Proxy/construct/trap-is-undefined-proto-from-ctor-realm.js": true,
- "test/built-ins/Proxy/construct/trap-is-not-callable-realm.js": true,
- // class
- "test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
- "test/language/statements/class/subclass/builtin-objects/Symbol/new-symbol-with-super-throws.js": true,
- "test/language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called.js": true,
- "test/language/statements/class/subclass/builtin-objects/WeakSet/regular-subclassing.js": true,
- "test/language/statements/class/subclass/builtin-objects/WeakMap/super-must-be-called.js": true,
- "test/language/statements/class/subclass/builtin-objects/WeakMap/regular-subclassing.js": true,
- "test/language/statements/class/subclass/builtin-objects/Map/super-must-be-called.js": true,
- "test/language/statements/class/subclass/builtin-objects/Map/regular-subclassing.js": true,
- "test/language/statements/class/subclass/builtin-objects/Set/super-must-be-called.js": true,
- "test/language/statements/class/subclass/builtin-objects/Set/regular-subclassing.js": true,
- "test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js": true,
- "test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js": true,
- "test/built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js": true,
- "test/language/statements/class/subclass/builtin-objects/Array/length.js": true,
- // full unicode regexp flag
- "test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js": true,
- "test/built-ins/RegExp/prototype/Symbol.match/get-unicode-error.js": true,
- "test/built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js": true,
- "test/built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js": true,
- // object literals
- "test/built-ins/Array/from/source-object-iterator-1.js": true,
- "test/built-ins/Array/from/source-object-iterator-2.js": true,
- // Typed arrays
- "test/built-ins/Array/from/items-is-arraybuffer.js": true,
- "test/built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js": true,
- "test/built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js": true,
- // for-of
- "test/language/statements/for-of/Array.prototype.keys.js": true,
- "test/language/statements/for-of/Array.prototype.entries.js": true,
- "test/language/statements/for-of/Array.prototype.Symbol.iterator.js": true,
- // arrow-function
- "test/built-ins/Object/prototype/toString/proxy-function.js": true,
- }
- featuresBlackList = []string{
- "arrow-function",
- }
- es6WhiteList = map[string]bool{}
- es6IdWhiteList = []string{
- "9.5",
- "12.9.3",
- "12.9.4",
- "19.1",
- "19.4",
- "21.1.3.14",
- "21.1.3.15",
- "21.1.3.17",
- "21.2.5.6",
- "22.1.2.1",
- "22.1.2.3",
- "22.1.2.5",
- "22.1.3",
- "22.1.4",
- "23.1",
- "23.2",
- "23.3",
- "23.4",
- "25.1.2",
- "26.1",
- "26.2",
- "B.2.1",
- "B.2.2",
- }
- )
- type tc39Test struct {
- name string
- f func(t *testing.T)
- }
- type tc39TestCtx struct {
- base string
- t *testing.T
- prgCache map[string]*Program
- prgCacheLock sync.Mutex
- testQueue []tc39Test
- }
- type TC39MetaNegative struct {
- Phase, Type string
- }
- type tc39Meta struct {
- Negative TC39MetaNegative
- Includes []string
- Flags []string
- Features []string
- Es5id string
- Es6id string
- Esid string
- }
- func (m *tc39Meta) hasFlag(flag string) bool {
- for _, f := range m.Flags {
- if f == flag {
- return true
- }
- }
- return false
- }
- func parseTC39File(name string) (*tc39Meta, string, error) {
- f, err := os.Open(name)
- if err != nil {
- return nil, "", err
- }
- defer f.Close()
- b, err := ioutil.ReadAll(f)
- if err != nil {
- return nil, "", err
- }
- str := string(b)
- metaStart := strings.Index(str, "/*---")
- if metaStart == -1 {
- return nil, "", invalidFormatError
- } else {
- metaStart += 5
- }
- metaEnd := strings.Index(str, "---*/")
- if metaEnd == -1 || metaEnd <= metaStart {
- return nil, "", invalidFormatError
- }
- var meta tc39Meta
- err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
- if err != nil {
- return nil, "", err
- }
- if meta.Negative.Type != "" && meta.Negative.Phase == "" {
- return nil, "", errors.New("negative type is set, but phase isn't")
- }
- return &meta, str, nil
- }
- func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.TB) {
- defer func() {
- if x := recover(); x != nil {
- panic(fmt.Sprintf("panic while running %s: %v", name, x))
- }
- }()
- vm := New()
- err, early := ctx.runTC39Script(name, src, meta.Includes, vm)
- if err != nil {
- if meta.Negative.Type == "" {
- t.Fatalf("%s: %v", name, err)
- } else {
- if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
- t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
- }
- var errType string
- switch err := err.(type) {
- case *Exception:
- if o, ok := err.Value().(*Object); ok {
- if c := o.Get("constructor"); c != nil {
- if c, ok := c.(*Object); ok {
- errType = c.Get("name").String()
- } else {
- t.Fatalf("%s: error constructor is not an object (%v)", name, o)
- }
- } else {
- t.Fatalf("%s: error does not have a constructor (%v)", name, o)
- }
- } else {
- t.Fatalf("%s: error is not an object (%v)", name, err.Value())
- }
- case *CompilerSyntaxError:
- errType = "SyntaxError"
- case *CompilerReferenceError:
- errType = "ReferenceError"
- default:
- t.Fatalf("%s: error is not a JS error: %v", name, err)
- }
- if errType != meta.Negative.Type {
- vm.vm.prg.dumpCode(t.Logf)
- t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
- }
- }
- } else {
- if meta.Negative.Type != "" {
- vm.vm.prg.dumpCode(t.Logf)
- t.Fatalf("%s: Expected error: %v", name, err)
- }
- }
- }
- func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
- if skipList[name] {
- t.Skip("Excluded")
- }
- p := path.Join(ctx.base, name)
- meta, src, err := parseTC39File(p)
- if err != nil {
- //t.Fatalf("Could not parse %s: %v", name, err)
- t.Errorf("Could not parse %s: %v", name, err)
- return
- }
- if meta.Es5id == "" {
- skip := true
- //t.Logf("%s: Not ES5, skipped", name)
- if es6WhiteList[name] {
- skip = false
- } else {
- if meta.Es6id != "" {
- for _, prefix := range es6IdWhiteList {
- if strings.HasPrefix(meta.Es6id, prefix) &&
- (len(meta.Es6id) == len(prefix) || meta.Es6id[len(prefix)] == '.') {
- skip = false
- break
- }
- }
- }
- }
- if skip {
- t.Skip("Not ES5")
- }
- for _, feature := range meta.Features {
- for _, bl := range featuresBlackList {
- if feature == bl {
- t.Skip("Blacklisted feature")
- }
- }
- }
- }
- hasRaw := meta.hasFlag("raw")
- if hasRaw || !meta.hasFlag("onlyStrict") {
- //log.Printf("Running normal test: %s", name)
- //t.Logf("Running normal test: %s", name)
- ctx.runTC39Test(name, src, meta, t)
- }
- if !hasRaw && !meta.hasFlag("noStrict") {
- //log.Printf("Running strict test: %s", name)
- //t.Logf("Running strict test: %s", name)
- ctx.runTC39Test(name, "'use strict';\n"+src, meta, t)
- }
- }
- func (ctx *tc39TestCtx) init() {
- ctx.prgCache = make(map[string]*Program)
- }
- func (ctx *tc39TestCtx) compile(base, name string) (*Program, error) {
- ctx.prgCacheLock.Lock()
- defer ctx.prgCacheLock.Unlock()
- prg := ctx.prgCache[name]
- if prg == nil {
- fname := path.Join(base, name)
- f, err := os.Open(fname)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- b, err := ioutil.ReadAll(f)
- if err != nil {
- return nil, err
- }
- str := string(b)
- prg, err = Compile(name, str, false)
- if err != nil {
- return nil, err
- }
- ctx.prgCache[name] = prg
- }
- return prg, nil
- }
- func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
- prg, err := ctx.compile(base, name)
- if err != nil {
- return err
- }
- _, err = vm.RunProgram(prg)
- return err
- }
- func (ctx *tc39TestCtx) runTC39Script(name, src string, includes []string, vm *Runtime) (err error, early bool) {
- early = true
- err = ctx.runFile(ctx.base, path.Join("harness", "assert.js"), vm)
- if err != nil {
- return
- }
- err = ctx.runFile(ctx.base, path.Join("harness", "sta.js"), vm)
- if err != nil {
- return
- }
- for _, include := range includes {
- err = ctx.runFile(ctx.base, path.Join("harness", include), vm)
- if err != nil {
- return
- }
- }
- var p *Program
- p, err = Compile(name, src, false)
- if err != nil {
- return
- }
- early = false
- _, err = vm.RunProgram(p)
- return
- }
- func (ctx *tc39TestCtx) runTC39Tests(name string) {
- files, err := ioutil.ReadDir(path.Join(ctx.base, name))
- if err != nil {
- ctx.t.Fatal(err)
- }
- for _, file := range files {
- if file.Name()[0] == '.' {
- continue
- }
- if file.IsDir() {
- ctx.runTC39Tests(path.Join(name, file.Name()))
- } else {
- if strings.HasSuffix(file.Name(), ".js") {
- name := path.Join(name, file.Name())
- ctx.runTest(name, func(t *testing.T) {
- ctx.runTC39File(name, t)
- })
- }
- }
- }
- }
- func TestTC39(t *testing.T) {
- if testing.Short() {
- t.Skip()
- }
- if _, err := os.Stat(tc39BASE); err != nil {
- t.Skipf("If you want to run tc39 tests, download them from https://github.com/tc39/test262 and put into %s. The last working commit is 1ba3a7c4a93fc93b3d0d7e4146f59934a896837d. (%v)", tc39BASE, err)
- }
- ctx := &tc39TestCtx{
- base: tc39BASE,
- t: t,
- }
- ctx.init()
- //ctx.runTC39File("test/language/types/number/8.5.1.js", t)
- //ctx.runTC39Tests("test/language")
- ctx.runTC39Tests("test/language/expressions")
- ctx.runTC39Tests("test/language/arguments-object")
- ctx.runTC39Tests("test/language/asi")
- ctx.runTC39Tests("test/language/directive-prologue")
- ctx.runTC39Tests("test/language/function-code")
- ctx.runTC39Tests("test/language/eval-code")
- ctx.runTC39Tests("test/language/global-code")
- ctx.runTC39Tests("test/language/identifier-resolution")
- ctx.runTC39Tests("test/language/identifiers")
- //ctx.runTC39Tests("test/language/literals") // octal sequences in strict mode
- ctx.runTC39Tests("test/language/punctuators")
- ctx.runTC39Tests("test/language/reserved-words")
- ctx.runTC39Tests("test/language/source-text")
- ctx.runTC39Tests("test/language/statements")
- ctx.runTC39Tests("test/language/types")
- ctx.runTC39Tests("test/language/white-space")
- ctx.runTC39Tests("test/built-ins")
- ctx.runTC39Tests("test/annexB/built-ins/String/prototype/substr")
- ctx.runTC39Tests("test/annexB/built-ins/escape")
- ctx.runTC39Tests("test/annexB/built-ins/unescape")
- ctx.flush()
- }
|