tc39_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package goja
  2. import (
  3. "errors"
  4. "gopkg.in/yaml.v2"
  5. "io/ioutil"
  6. "os"
  7. "path"
  8. "strings"
  9. "testing"
  10. )
  11. const (
  12. tc39BASE = "testdata/test262"
  13. )
  14. var (
  15. invalidFormatError = errors.New("Invalid file format")
  16. )
  17. var (
  18. skipList = map[string]bool{
  19. "test/language/literals/regexp/S7.8.5_A1.1_T2.js": true, // UTF-16
  20. "test/language/literals/regexp/S7.8.5_A1.4_T2.js": true, // UTF-16
  21. "test/language/literals/regexp/S7.8.5_A2.1_T2.js": true, // UTF-16
  22. "test/language/literals/regexp/S7.8.5_A2.4_T2.js": true, // UTF-16
  23. "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js": true, // timezone
  24. "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
  25. "test/built-ins/Object/getOwnPropertyNames/15.2.3.4-4-44.js": true, // property order
  26. }
  27. )
  28. type tc39TestCtx struct {
  29. prgCache map[string]*Program
  30. }
  31. type TC39MetaNegative struct {
  32. Phase, Type string
  33. }
  34. type tc39Meta struct {
  35. Negative TC39MetaNegative
  36. Includes []string
  37. Flags []string
  38. Es5id string
  39. Es6id string
  40. Esid string
  41. }
  42. func (m *tc39Meta) hasFlag(flag string) bool {
  43. for _, f := range m.Flags {
  44. if f == flag {
  45. return true
  46. }
  47. }
  48. return false
  49. }
  50. func parseTC39File(name string) (*tc39Meta, string, error) {
  51. f, err := os.Open(name)
  52. if err != nil {
  53. return nil, "", err
  54. }
  55. defer f.Close()
  56. b, err := ioutil.ReadAll(f)
  57. if err != nil {
  58. return nil, "", err
  59. }
  60. str := string(b)
  61. metaStart := strings.Index(str, "/*---")
  62. if metaStart == -1 {
  63. return nil, "", invalidFormatError
  64. } else {
  65. metaStart += 5
  66. }
  67. metaEnd := strings.Index(str, "---*/")
  68. if metaEnd == -1 || metaEnd <= metaStart {
  69. return nil, "", invalidFormatError
  70. }
  71. var meta tc39Meta
  72. err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
  73. if err != nil {
  74. return nil, "", err
  75. }
  76. if meta.Negative.Type != "" && meta.Negative.Phase == "" {
  77. return nil, "", errors.New("negative type is set, but phase isn't")
  78. }
  79. return &meta, str, nil
  80. }
  81. func runTC39Test(base, name, src string, meta *tc39Meta, t testing.TB, ctx *tc39TestCtx) {
  82. vm := New()
  83. err, early := runTC39Script(base, name, src, meta.Includes, t, ctx, vm)
  84. if err != nil {
  85. if meta.Negative.Type == "" {
  86. t.Fatalf("%s: %v", name, err)
  87. } else {
  88. if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
  89. t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
  90. }
  91. var errType string
  92. switch err := err.(type) {
  93. case *Exception:
  94. if o, ok := err.Value().(*Object); ok {
  95. if c := o.Get("constructor"); c != nil {
  96. if c, ok := c.(*Object); ok {
  97. errType = c.Get("name").String()
  98. } else {
  99. t.Fatalf("%s: error constructor is not an object (%v)", name, o)
  100. }
  101. } else {
  102. t.Fatalf("%s: error does not have a constructor (%v)", name, o)
  103. }
  104. } else {
  105. t.Fatalf("%s: error is not an object (%v)", name, err.Value())
  106. }
  107. case *CompilerSyntaxError:
  108. errType = "SyntaxError"
  109. case *CompilerReferenceError:
  110. errType = "ReferenceError"
  111. default:
  112. t.Fatalf("%s: error is not a JS error: %v", name, err)
  113. }
  114. if errType != meta.Negative.Type {
  115. vm.vm.prg.dumpCode(t.Logf)
  116. t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
  117. }
  118. }
  119. } else {
  120. if meta.Negative.Type != "" {
  121. vm.vm.prg.dumpCode(t.Logf)
  122. t.Fatalf("%s: Expected error: %v", name, err)
  123. }
  124. }
  125. }
  126. func runTC39File(base, name string, t testing.TB, ctx *tc39TestCtx) {
  127. if skipList[name] {
  128. t.Logf("Skipped %s", name)
  129. return
  130. }
  131. p := path.Join(base, name)
  132. meta, src, err := parseTC39File(p)
  133. if err != nil {
  134. //t.Fatalf("Could not parse %s: %v", name, err)
  135. t.Errorf("Could not parse %s: %v", name, err)
  136. return
  137. }
  138. if meta.Es5id == "" {
  139. //t.Logf("%s: Not ES5, skipped", name)
  140. return
  141. }
  142. hasRaw := meta.hasFlag("raw")
  143. if hasRaw || !meta.hasFlag("onlyStrict") {
  144. //log.Printf("Running normal test: %s", name)
  145. //t.Logf("Running normal test: %s", name)
  146. runTC39Test(base, name, src, meta, t, ctx)
  147. }
  148. if !hasRaw && !meta.hasFlag("noStrict") {
  149. //log.Printf("Running strict test: %s", name)
  150. //t.Logf("Running strict test: %s", name)
  151. runTC39Test(base, name, "'use strict';\n"+src, meta, t, ctx)
  152. }
  153. }
  154. func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
  155. prg := ctx.prgCache[name]
  156. if prg == nil {
  157. fname := path.Join(base, name)
  158. f, err := os.Open(fname)
  159. if err != nil {
  160. return err
  161. }
  162. defer f.Close()
  163. b, err := ioutil.ReadAll(f)
  164. if err != nil {
  165. return err
  166. }
  167. str := string(b)
  168. prg, err = Compile(name, str, false)
  169. if err != nil {
  170. return err
  171. }
  172. ctx.prgCache[name] = prg
  173. }
  174. _, err := vm.RunProgram(prg)
  175. return err
  176. }
  177. func runTC39Script(base, name, src string, includes []string, t testing.TB, ctx *tc39TestCtx, vm *Runtime) (err error, early bool) {
  178. early = true
  179. err = ctx.runFile(base, path.Join("harness", "assert.js"), vm)
  180. if err != nil {
  181. return
  182. }
  183. err = ctx.runFile(base, path.Join("harness", "sta.js"), vm)
  184. if err != nil {
  185. return
  186. }
  187. for _, include := range includes {
  188. err = ctx.runFile(base, path.Join("harness", include), vm)
  189. if err != nil {
  190. return
  191. }
  192. }
  193. var p *Program
  194. p, err = Compile(name, src, false)
  195. if err != nil {
  196. return
  197. }
  198. early = false
  199. _, err = vm.RunProgram(p)
  200. return
  201. }
  202. func runTC39Tests(base, name string, t *testing.T, ctx *tc39TestCtx) {
  203. files, err := ioutil.ReadDir(path.Join(base, name))
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. for _, file := range files {
  208. if file.Name()[0] == '.' {
  209. continue
  210. }
  211. if file.IsDir() {
  212. runTC39Tests(base, path.Join(name, file.Name()), t, ctx)
  213. } else {
  214. if strings.HasSuffix(file.Name(), ".js") {
  215. runTC39File(base, path.Join(name, file.Name()), t, ctx)
  216. }
  217. }
  218. }
  219. }
  220. func TestTC39(t *testing.T) {
  221. if testing.Short() {
  222. t.Skip()
  223. }
  224. if _, err := os.Stat(tc39BASE); err != nil {
  225. 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)
  226. }
  227. ctx := &tc39TestCtx{
  228. prgCache: make(map[string]*Program),
  229. }
  230. //_ = "breakpoint"
  231. //runTC39File(tc39BASE, "test/language/types/number/8.5.1.js", t, ctx)
  232. //runTC39Tests(tc39BASE, "test/language", t, ctx)
  233. runTC39Tests(tc39BASE, "test/language/expressions", t, ctx)
  234. runTC39Tests(tc39BASE, "test/language/arguments-object", t, ctx)
  235. runTC39Tests(tc39BASE, "test/language/asi", t, ctx)
  236. runTC39Tests(tc39BASE, "test/language/directive-prologue", t, ctx)
  237. runTC39Tests(tc39BASE, "test/language/function-code", t, ctx)
  238. runTC39Tests(tc39BASE, "test/language/eval-code", t, ctx)
  239. runTC39Tests(tc39BASE, "test/language/global-code", t, ctx)
  240. runTC39Tests(tc39BASE, "test/language/identifier-resolution", t, ctx)
  241. runTC39Tests(tc39BASE, "test/language/identifiers", t, ctx)
  242. //runTC39Tests(tc39BASE, "test/language/literals", t, ctx) // octal sequences in strict mode
  243. runTC39Tests(tc39BASE, "test/language/punctuators", t, ctx)
  244. runTC39Tests(tc39BASE, "test/language/reserved-words", t, ctx)
  245. runTC39Tests(tc39BASE, "test/language/source-text", t, ctx)
  246. runTC39Tests(tc39BASE, "test/language/statements", t, ctx)
  247. runTC39Tests(tc39BASE, "test/language/types", t, ctx)
  248. runTC39Tests(tc39BASE, "test/language/white-space", t, ctx)
  249. runTC39Tests(tc39BASE, "test/built-ins", t, ctx)
  250. runTC39Tests(tc39BASE, "test/annexB/built-ins/String/prototype/substr", t, ctx)
  251. }