tc39_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. package goja
  2. import (
  3. "errors"
  4. "fmt"
  5. "gopkg.in/yaml.v2"
  6. "io/ioutil"
  7. "os"
  8. "path"
  9. "strings"
  10. "sync"
  11. "testing"
  12. )
  13. const (
  14. tc39BASE = "testdata/test262"
  15. )
  16. var (
  17. invalidFormatError = errors.New("Invalid file format")
  18. )
  19. var (
  20. skipList = map[string]bool{
  21. "test/language/literals/regexp/S7.8.5_A1.1_T2.js": true, // UTF-16
  22. "test/language/literals/regexp/S7.8.5_A1.4_T2.js": true, // UTF-16
  23. "test/language/literals/regexp/S7.8.5_A2.1_T2.js": true, // UTF-16
  24. "test/language/literals/regexp/S7.8.5_A2.4_T2.js": true, // UTF-16
  25. "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js": true, // timezone
  26. "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js": true, // timezone
  27. "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
  28. "test/annexB/built-ins/escape/escape-above-astral.js": true, // \u{xxxxx}
  29. // utf-16
  30. "test/built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-string-wrapper.js": true,
  31. // cross-realm
  32. "test/built-ins/Symbol/unscopables/cross-realm.js": true,
  33. "test/built-ins/Symbol/toStringTag/cross-realm.js": true,
  34. "test/built-ins/Symbol/toPrimitive/cross-realm.js": true,
  35. "test/built-ins/Symbol/split/cross-realm.js": true,
  36. "test/built-ins/Symbol/species/cross-realm.js": true,
  37. "test/built-ins/Symbol/search/cross-realm.js": true,
  38. "test/built-ins/Symbol/replace/cross-realm.js": true,
  39. "test/built-ins/Symbol/match/cross-realm.js": true,
  40. "test/built-ins/Symbol/keyFor/cross-realm.js": true,
  41. "test/built-ins/Symbol/iterator/cross-realm.js": true,
  42. "test/built-ins/Symbol/isConcatSpreadable/cross-realm.js": true,
  43. "test/built-ins/Symbol/hasInstance/cross-realm.js": true,
  44. "test/built-ins/Symbol/for/cross-realm.js": true,
  45. "test/built-ins/WeakSet/proto-from-ctor-realm.js": true,
  46. "test/built-ins/WeakMap/proto-from-ctor-realm.js": true,
  47. "test/built-ins/Map/proto-from-ctor-realm.js": true,
  48. "test/built-ins/Set/proto-from-ctor-realm.js": true,
  49. "test/built-ins/Object/proto-from-ctor.js": true,
  50. "test/built-ins/Array/from/proto-from-ctor-realm.js": true,
  51. "test/built-ins/Array/of/proto-from-ctor-realm.js": true,
  52. "test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-non-array.js": true,
  53. "test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-array.js": true,
  54. "test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-non-array.js": true,
  55. "test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js": true,
  56. "test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-non-array.js": true,
  57. "test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-array.js": true,
  58. "test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-non-array.js": true,
  59. "test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-array.js": true,
  60. "test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-non-array.js": true,
  61. "test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-array.js": true,
  62. "test/built-ins/Proxy/construct/arguments-realm.js": true,
  63. "test/built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js": true,
  64. "test/built-ins/Proxy/getPrototypeOf/trap-is-not-callable-realm.js": true,
  65. "test/built-ins/Proxy/set/trap-is-not-callable-realm.js": true,
  66. "test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-not-callable-realm.js": true,
  67. "test/built-ins/Proxy/getOwnPropertyDescriptor/result-type-is-not-object-nor-undefined-realm.js": true,
  68. "test/built-ins/Proxy/get/trap-is-not-callable-realm.js": true,
  69. "test/built-ins/Proxy/preventExtensions/trap-is-not-callable-realm.js": true,
  70. "test/built-ins/Proxy/defineProperty/null-handler-realm.js": true,
  71. "test/built-ins/Proxy/ownKeys/trap-is-not-callable-realm.js": true,
  72. "test/built-ins/Proxy/ownKeys/return-not-list-object-throws-realm.js": true,
  73. "test/built-ins/Proxy/deleteProperty/trap-is-not-callable-realm.js": true,
  74. "test/built-ins/Proxy/isExtensible/trap-is-not-callable-realm.js": true,
  75. "test/built-ins/Proxy/defineProperty/trap-is-not-callable-realm.js": true,
  76. "test/built-ins/Proxy/defineProperty/targetdesc-undefined-target-is-not-extensible-realm.js": true,
  77. "test/built-ins/Proxy/defineProperty/targetdesc-undefined-not-configurable-descriptor-realm.js": true,
  78. "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor.js": true,
  79. "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-realm.js": true,
  80. "test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-not-configurable-target-realm.js": true,
  81. "test/built-ins/Proxy/defineProperty/targetdesc-configurable-desc-not-configurable-realm.js": true,
  82. "test/built-ins/Proxy/has/trap-is-not-callable-realm.js": true,
  83. "test/built-ins/Proxy/defineProperty/desc-realm.js": true,
  84. "test/built-ins/Proxy/apply/trap-is-not-callable-realm.js": true,
  85. "test/built-ins/Proxy/apply/arguments-realm.js": true,
  86. "test/built-ins/Proxy/construct/trap-is-undefined-proto-from-ctor-realm.js": true,
  87. "test/built-ins/Proxy/construct/trap-is-not-callable-realm.js": true,
  88. // class
  89. "test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
  90. "test/language/statements/class/subclass/builtin-objects/Symbol/new-symbol-with-super-throws.js": true,
  91. "test/language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called.js": true,
  92. "test/language/statements/class/subclass/builtin-objects/WeakSet/regular-subclassing.js": true,
  93. "test/language/statements/class/subclass/builtin-objects/WeakMap/super-must-be-called.js": true,
  94. "test/language/statements/class/subclass/builtin-objects/WeakMap/regular-subclassing.js": true,
  95. "test/language/statements/class/subclass/builtin-objects/Map/super-must-be-called.js": true,
  96. "test/language/statements/class/subclass/builtin-objects/Map/regular-subclassing.js": true,
  97. "test/language/statements/class/subclass/builtin-objects/Set/super-must-be-called.js": true,
  98. "test/language/statements/class/subclass/builtin-objects/Set/regular-subclassing.js": true,
  99. "test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js": true,
  100. "test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js": true,
  101. "test/built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js": true,
  102. "test/language/statements/class/subclass/builtin-objects/Array/length.js": true,
  103. // full unicode regexp flag
  104. "test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js": true,
  105. "test/built-ins/RegExp/prototype/Symbol.match/get-unicode-error.js": true,
  106. "test/built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js": true,
  107. "test/built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js": true,
  108. // object literals
  109. "test/built-ins/Array/from/source-object-iterator-1.js": true,
  110. "test/built-ins/Array/from/source-object-iterator-2.js": true,
  111. // Typed arrays
  112. "test/built-ins/Array/from/items-is-arraybuffer.js": true,
  113. "test/built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js": true,
  114. "test/built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js": true,
  115. // for-of
  116. "test/language/statements/for-of/Array.prototype.keys.js": true,
  117. "test/language/statements/for-of/Array.prototype.entries.js": true,
  118. "test/language/statements/for-of/Array.prototype.Symbol.iterator.js": true,
  119. // arrow-function
  120. "test/built-ins/Object/prototype/toString/proxy-function.js": true,
  121. }
  122. featuresBlackList = []string{
  123. "arrow-function",
  124. }
  125. es6WhiteList = map[string]bool{}
  126. es6IdWhiteList = []string{
  127. "9.5",
  128. "12.9.3",
  129. "12.9.4",
  130. "19.1",
  131. "19.4",
  132. "21.1.3.14",
  133. "21.1.3.15",
  134. "21.1.3.17",
  135. "21.2.5.6",
  136. "22.1.2.1",
  137. "22.1.2.3",
  138. "22.1.2.5",
  139. "22.1.3",
  140. "22.1.4",
  141. "23.1",
  142. "23.2",
  143. "23.3",
  144. "23.4",
  145. "25.1.2",
  146. "26.1",
  147. "26.2",
  148. "B.2.1",
  149. "B.2.2",
  150. }
  151. )
  152. type tc39Test struct {
  153. name string
  154. f func(t *testing.T)
  155. }
  156. type tc39TestCtx struct {
  157. base string
  158. t *testing.T
  159. prgCache map[string]*Program
  160. prgCacheLock sync.Mutex
  161. testQueue []tc39Test
  162. }
  163. type TC39MetaNegative struct {
  164. Phase, Type string
  165. }
  166. type tc39Meta struct {
  167. Negative TC39MetaNegative
  168. Includes []string
  169. Flags []string
  170. Features []string
  171. Es5id string
  172. Es6id string
  173. Esid string
  174. }
  175. func (m *tc39Meta) hasFlag(flag string) bool {
  176. for _, f := range m.Flags {
  177. if f == flag {
  178. return true
  179. }
  180. }
  181. return false
  182. }
  183. func parseTC39File(name string) (*tc39Meta, string, error) {
  184. f, err := os.Open(name)
  185. if err != nil {
  186. return nil, "", err
  187. }
  188. defer f.Close()
  189. b, err := ioutil.ReadAll(f)
  190. if err != nil {
  191. return nil, "", err
  192. }
  193. str := string(b)
  194. metaStart := strings.Index(str, "/*---")
  195. if metaStart == -1 {
  196. return nil, "", invalidFormatError
  197. } else {
  198. metaStart += 5
  199. }
  200. metaEnd := strings.Index(str, "---*/")
  201. if metaEnd == -1 || metaEnd <= metaStart {
  202. return nil, "", invalidFormatError
  203. }
  204. var meta tc39Meta
  205. err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
  206. if err != nil {
  207. return nil, "", err
  208. }
  209. if meta.Negative.Type != "" && meta.Negative.Phase == "" {
  210. return nil, "", errors.New("negative type is set, but phase isn't")
  211. }
  212. return &meta, str, nil
  213. }
  214. func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.TB) {
  215. defer func() {
  216. if x := recover(); x != nil {
  217. panic(fmt.Sprintf("panic while running %s: %v", name, x))
  218. }
  219. }()
  220. vm := New()
  221. err, early := ctx.runTC39Script(name, src, meta.Includes, vm)
  222. if err != nil {
  223. if meta.Negative.Type == "" {
  224. t.Fatalf("%s: %v", name, err)
  225. } else {
  226. if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
  227. t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
  228. }
  229. var errType string
  230. switch err := err.(type) {
  231. case *Exception:
  232. if o, ok := err.Value().(*Object); ok {
  233. if c := o.Get("constructor"); c != nil {
  234. if c, ok := c.(*Object); ok {
  235. errType = c.Get("name").String()
  236. } else {
  237. t.Fatalf("%s: error constructor is not an object (%v)", name, o)
  238. }
  239. } else {
  240. t.Fatalf("%s: error does not have a constructor (%v)", name, o)
  241. }
  242. } else {
  243. t.Fatalf("%s: error is not an object (%v)", name, err.Value())
  244. }
  245. case *CompilerSyntaxError:
  246. errType = "SyntaxError"
  247. case *CompilerReferenceError:
  248. errType = "ReferenceError"
  249. default:
  250. t.Fatalf("%s: error is not a JS error: %v", name, err)
  251. }
  252. if errType != meta.Negative.Type {
  253. vm.vm.prg.dumpCode(t.Logf)
  254. t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
  255. }
  256. }
  257. } else {
  258. if meta.Negative.Type != "" {
  259. vm.vm.prg.dumpCode(t.Logf)
  260. t.Fatalf("%s: Expected error: %v", name, err)
  261. }
  262. }
  263. }
  264. func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
  265. if skipList[name] {
  266. t.Skip("Excluded")
  267. }
  268. p := path.Join(ctx.base, name)
  269. meta, src, err := parseTC39File(p)
  270. if err != nil {
  271. //t.Fatalf("Could not parse %s: %v", name, err)
  272. t.Errorf("Could not parse %s: %v", name, err)
  273. return
  274. }
  275. if meta.Es5id == "" {
  276. skip := true
  277. //t.Logf("%s: Not ES5, skipped", name)
  278. if es6WhiteList[name] {
  279. skip = false
  280. } else {
  281. if meta.Es6id != "" {
  282. for _, prefix := range es6IdWhiteList {
  283. if strings.HasPrefix(meta.Es6id, prefix) &&
  284. (len(meta.Es6id) == len(prefix) || meta.Es6id[len(prefix)] == '.') {
  285. skip = false
  286. break
  287. }
  288. }
  289. }
  290. }
  291. if skip {
  292. t.Skip("Not ES5")
  293. }
  294. for _, feature := range meta.Features {
  295. for _, bl := range featuresBlackList {
  296. if feature == bl {
  297. t.Skip("Blacklisted feature")
  298. }
  299. }
  300. }
  301. }
  302. hasRaw := meta.hasFlag("raw")
  303. if hasRaw || !meta.hasFlag("onlyStrict") {
  304. //log.Printf("Running normal test: %s", name)
  305. //t.Logf("Running normal test: %s", name)
  306. ctx.runTC39Test(name, src, meta, t)
  307. }
  308. if !hasRaw && !meta.hasFlag("noStrict") {
  309. //log.Printf("Running strict test: %s", name)
  310. //t.Logf("Running strict test: %s", name)
  311. ctx.runTC39Test(name, "'use strict';\n"+src, meta, t)
  312. }
  313. }
  314. func (ctx *tc39TestCtx) init() {
  315. ctx.prgCache = make(map[string]*Program)
  316. }
  317. func (ctx *tc39TestCtx) compile(base, name string) (*Program, error) {
  318. ctx.prgCacheLock.Lock()
  319. defer ctx.prgCacheLock.Unlock()
  320. prg := ctx.prgCache[name]
  321. if prg == nil {
  322. fname := path.Join(base, name)
  323. f, err := os.Open(fname)
  324. if err != nil {
  325. return nil, err
  326. }
  327. defer f.Close()
  328. b, err := ioutil.ReadAll(f)
  329. if err != nil {
  330. return nil, err
  331. }
  332. str := string(b)
  333. prg, err = Compile(name, str, false)
  334. if err != nil {
  335. return nil, err
  336. }
  337. ctx.prgCache[name] = prg
  338. }
  339. return prg, nil
  340. }
  341. func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
  342. prg, err := ctx.compile(base, name)
  343. if err != nil {
  344. return err
  345. }
  346. _, err = vm.RunProgram(prg)
  347. return err
  348. }
  349. func (ctx *tc39TestCtx) runTC39Script(name, src string, includes []string, vm *Runtime) (err error, early bool) {
  350. early = true
  351. err = ctx.runFile(ctx.base, path.Join("harness", "assert.js"), vm)
  352. if err != nil {
  353. return
  354. }
  355. err = ctx.runFile(ctx.base, path.Join("harness", "sta.js"), vm)
  356. if err != nil {
  357. return
  358. }
  359. for _, include := range includes {
  360. err = ctx.runFile(ctx.base, path.Join("harness", include), vm)
  361. if err != nil {
  362. return
  363. }
  364. }
  365. var p *Program
  366. p, err = Compile(name, src, false)
  367. if err != nil {
  368. return
  369. }
  370. early = false
  371. _, err = vm.RunProgram(p)
  372. return
  373. }
  374. func (ctx *tc39TestCtx) runTC39Tests(name string) {
  375. files, err := ioutil.ReadDir(path.Join(ctx.base, name))
  376. if err != nil {
  377. ctx.t.Fatal(err)
  378. }
  379. for _, file := range files {
  380. if file.Name()[0] == '.' {
  381. continue
  382. }
  383. if file.IsDir() {
  384. ctx.runTC39Tests(path.Join(name, file.Name()))
  385. } else {
  386. if strings.HasSuffix(file.Name(), ".js") {
  387. name := path.Join(name, file.Name())
  388. ctx.runTest(name, func(t *testing.T) {
  389. ctx.runTC39File(name, t)
  390. })
  391. }
  392. }
  393. }
  394. }
  395. func TestTC39(t *testing.T) {
  396. if testing.Short() {
  397. t.Skip()
  398. }
  399. if _, err := os.Stat(tc39BASE); err != nil {
  400. 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)
  401. }
  402. ctx := &tc39TestCtx{
  403. base: tc39BASE,
  404. t: t,
  405. }
  406. ctx.init()
  407. //ctx.runTC39File("test/language/types/number/8.5.1.js", t)
  408. //ctx.runTC39Tests("test/language")
  409. ctx.runTC39Tests("test/language/expressions")
  410. ctx.runTC39Tests("test/language/arguments-object")
  411. ctx.runTC39Tests("test/language/asi")
  412. ctx.runTC39Tests("test/language/directive-prologue")
  413. ctx.runTC39Tests("test/language/function-code")
  414. ctx.runTC39Tests("test/language/eval-code")
  415. ctx.runTC39Tests("test/language/global-code")
  416. ctx.runTC39Tests("test/language/identifier-resolution")
  417. ctx.runTC39Tests("test/language/identifiers")
  418. //ctx.runTC39Tests("test/language/literals") // octal sequences in strict mode
  419. ctx.runTC39Tests("test/language/punctuators")
  420. ctx.runTC39Tests("test/language/reserved-words")
  421. ctx.runTC39Tests("test/language/source-text")
  422. ctx.runTC39Tests("test/language/statements")
  423. ctx.runTC39Tests("test/language/types")
  424. ctx.runTC39Tests("test/language/white-space")
  425. ctx.runTC39Tests("test/built-ins")
  426. ctx.runTC39Tests("test/annexB/built-ins/String/prototype/substr")
  427. ctx.runTC39Tests("test/annexB/built-ins/escape")
  428. ctx.runTC39Tests("test/annexB/built-ins/unescape")
  429. ctx.flush()
  430. }