1 package goja
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "os"
8 "path"
9 "sort"
10 "strings"
11 "sync"
12 "testing"
13 "time"
14
15 "gopkg.in/yaml.v2"
16 )
17
18 const (
19 tc39BASE = "testdata/test262"
20 )
21
22 var (
23 invalidFormatError = errors.New("Invalid file format")
24
25 ignorableTestError = newSymbol(stringEmpty)
26 )
27
28 var (
29 skipPrefixes prefixList
30
31 skipList = map[string]bool{
32
33
34 "test/language/expressions/prefix-increment/S11.4.4_A6_T3.js": true,
35 "test/language/expressions/prefix-increment/S11.4.4_A6_T2.js": true,
36 "test/language/expressions/prefix-increment/S11.4.4_A6_T1.js": true,
37 "test/language/expressions/prefix-decrement/S11.4.5_A6_T3.js": true,
38 "test/language/expressions/prefix-decrement/S11.4.5_A6_T2.js": true,
39 "test/language/expressions/prefix-decrement/S11.4.5_A6_T1.js": true,
40 "test/language/expressions/postfix-increment/S11.3.1_A6_T3.js": true,
41 "test/language/expressions/postfix-increment/S11.3.1_A6_T2.js": true,
42 "test/language/expressions/postfix-increment/S11.3.1_A6_T1.js": true,
43 "test/language/expressions/postfix-decrement/S11.3.2_A6_T3.js": true,
44 "test/language/expressions/postfix-decrement/S11.3.2_A6_T2.js": true,
45 "test/language/expressions/postfix-decrement/S11.3.2_A6_T1.js": true,
46 "test/language/expressions/compound-assignment/S11.13.2_A7.1_T4.js": true,
47 "test/language/expressions/compound-assignment/S11.13.2_A7.1_T2.js": true,
48 "test/language/expressions/compound-assignment/S11.13.2_A7.1_T1.js": true,
49 "test/language/expressions/compound-assignment/S11.13.2_A7.11_T4.js": true,
50 "test/language/expressions/compound-assignment/S11.13.2_A7.11_T2.js": true,
51 "test/language/expressions/compound-assignment/S11.13.2_A7.11_T1.js": true,
52 "test/language/expressions/compound-assignment/S11.13.2_A7.10_T4.js": true,
53 "test/language/expressions/compound-assignment/S11.13.2_A7.10_T2.js": true,
54 "test/language/expressions/compound-assignment/S11.13.2_A7.10_T1.js": true,
55 "test/language/expressions/compound-assignment/S11.13.2_A7.9_T4.js": true,
56 "test/language/expressions/compound-assignment/S11.13.2_A7.9_T2.js": true,
57 "test/language/expressions/compound-assignment/S11.13.2_A7.9_T1.js": true,
58 "test/language/expressions/compound-assignment/S11.13.2_A7.8_T4.js": true,
59 "test/language/expressions/compound-assignment/S11.13.2_A7.8_T2.js": true,
60 "test/language/expressions/compound-assignment/S11.13.2_A7.8_T1.js": true,
61 "test/language/expressions/compound-assignment/S11.13.2_A7.7_T4.js": true,
62 "test/language/expressions/compound-assignment/S11.13.2_A7.7_T2.js": true,
63 "test/language/expressions/compound-assignment/S11.13.2_A7.7_T1.js": true,
64 "test/language/expressions/compound-assignment/S11.13.2_A7.6_T4.js": true,
65 "test/language/expressions/compound-assignment/S11.13.2_A7.6_T2.js": true,
66 "test/language/expressions/compound-assignment/S11.13.2_A7.6_T1.js": true,
67 "test/language/expressions/compound-assignment/S11.13.2_A7.5_T4.js": true,
68 "test/language/expressions/compound-assignment/S11.13.2_A7.5_T2.js": true,
69 "test/language/expressions/compound-assignment/S11.13.2_A7.5_T1.js": true,
70 "test/language/expressions/compound-assignment/S11.13.2_A7.4_T4.js": true,
71 "test/language/expressions/compound-assignment/S11.13.2_A7.4_T2.js": true,
72 "test/language/expressions/compound-assignment/S11.13.2_A7.4_T1.js": true,
73 "test/language/expressions/compound-assignment/S11.13.2_A7.3_T4.js": true,
74 "test/language/expressions/compound-assignment/S11.13.2_A7.3_T2.js": true,
75 "test/language/expressions/compound-assignment/S11.13.2_A7.3_T1.js": true,
76 "test/language/expressions/compound-assignment/S11.13.2_A7.2_T4.js": true,
77 "test/language/expressions/compound-assignment/S11.13.2_A7.2_T2.js": true,
78 "test/language/expressions/compound-assignment/S11.13.2_A7.2_T1.js": true,
79 "test/language/expressions/assignment/S11.13.1_A7_T3.js": true,
80
81
82 "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js": true,
83 "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js": true,
84 "test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true,
85
86
87 "test/built-ins/Date/UTC/fp-evaluation-order.js": true,
88
89
90 "test/built-ins/RegExp/quantifier-integer-limit.js": true,
91
92
93 "test/built-ins/Function/internals/Construct/base-ctor-revoked-proxy.js": true,
94
95
96 "test/language/expressions/class/elements/private-getter-is-not-a-own-property.js": true,
97 "test/language/expressions/class/elements/private-setter-is-not-a-own-property.js": true,
98 "test/language/statements/class/elements/private-setter-is-not-a-own-property.js": true,
99 "test/language/statements/class/elements/private-getter-is-not-a-own-property.js": true,
100
101
102 "test/built-ins/RegExp/unicode_restricted_quantifiable_assertion.js": true,
103 "test/built-ins/RegExp/unicode_restricted_octal_escape.js": true,
104 "test/built-ins/RegExp/unicode_restricted_incomple_quantifier.js": true,
105 "test/built-ins/RegExp/unicode_restricted_incomplete_quantifier.js": true,
106 "test/built-ins/RegExp/unicode_restricted_identity_escape_x.js": true,
107 "test/built-ins/RegExp/unicode_restricted_identity_escape_u.js": true,
108 "test/built-ins/RegExp/unicode_restricted_identity_escape_c.js": true,
109 "test/built-ins/RegExp/unicode_restricted_identity_escape_alpha.js": true,
110 "test/built-ins/RegExp/unicode_restricted_identity_escape.js": true,
111 "test/built-ins/RegExp/unicode_restricted_brackets.js": true,
112 "test/built-ins/RegExp/unicode_restricted_character_class_escape.js": true,
113 "test/annexB/built-ins/RegExp/prototype/compile/pattern-string-invalid-u.js": true,
114
115
116
117
118
119 "test/annexB/built-ins/RegExp/RegExp-leading-escape-BMP.js": true,
120 "test/annexB/built-ins/RegExp/RegExp-trailing-escape-BMP.js": true,
121 "test/language/literals/regexp/S7.8.5_A1.4_T2.js": true,
122 "test/language/literals/regexp/S7.8.5_A1.1_T2.js": true,
123 "test/language/literals/regexp/S7.8.5_A2.1_T2.js": true,
124 "test/language/literals/regexp/S7.8.5_A2.4_T2.js": true,
125
126
127 "test/language/expressions/optional-chaining/member-expression.js": true,
128 "test/language/expressions/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": true,
129 "test/language/expressions/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier.js": true,
130 "test/language/destructuring/binding/syntax/destructuring-object-parameters-function-arguments-length.js": true,
131 "test/language/destructuring/binding/syntax/destructuring-array-parameters-function-arguments-length.js": true,
132 "test/language/comments/hashbang/function-constructor.js": true,
133 "test/language/statements/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier.js": true,
134 "test/language/statements/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": true,
135 "test/language/statements/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier.js": true,
136 "test/language/statements/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": true,
137 "test/language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": true,
138 "test/language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier.js": true,
139 "test/built-ins/Object/seal/seal-asyncgeneratorfunction.js": true,
140 "test/language/statements/switch/scope-lex-async-generator.js": true,
141 "test/language/statements/class/elements/private-async-generator-method-name.js": true,
142 "test/language/expressions/class/elements/private-async-generator-method-name.js": true,
143 "test/language/expressions/async-generator/name.js": true,
144 "test/language/statements/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
145 "test/language/statements/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
146 "test/language/statements/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
147 "test/language/statements/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
148 "test/language/statements/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier.js": true,
149 "test/language/statements/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
150 "test/language/statements/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
151 "test/language/statements/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
152 "test/language/expressions/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
153 "test/language/expressions/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
154 "test/language/expressions/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
155 "test/language/expressions/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
156 "test/language/expressions/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier.js": true,
157 "test/language/expressions/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
158 "test/language/expressions/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier.js": true,
159 "test/language/expressions/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": true,
160 "test/built-ins/GeneratorFunction/is-a-constructor.js": true,
161
162
163 "test/language/expressions/optional-chaining/iteration-statement-for-await-of.js": true,
164
165
166 "test/language/literals/numeric/non-octal-decimal-integer.js": true,
167 "test/language/literals/string/S7.8.4_A4.3_T2.js": true,
168 "test/language/literals/string/S7.8.4_A4.3_T1.js": true,
169
170
171 "test/language/expressions/object/cpn-obj-lit-computed-property-name-from-integer-separators.js": true,
172 "test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-integer-separators.js": true,
173 "test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-integer-separators.js": true,
174 "test/language/statements/class/cpn-class-decl-computed-property-name-from-integer-separators.js": true,
175 "test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-integer-separators.js": true,
176 "test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-integer-separators.js": true,
177 "test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-integer-separators.js": true,
178 "test/language/expressions/class/cpn-class-expr-computed-property-name-from-integer-separators.js": true,
179 "test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-integer-separators.js": true,
180
181
182 "test/built-ins/Object/seal/seal-biguint64array.js": true,
183 "test/built-ins/Object/seal/seal-bigint64array.js": true,
184
185
186 "test/language/literals/regexp/invalid-range-negative-lookbehind.js": true,
187 "test/language/literals/regexp/invalid-range-lookbehind.js": true,
188 "test/language/literals/regexp/invalid-optional-negative-lookbehind.js": true,
189 "test/language/literals/regexp/invalid-optional-lookbehind.js": true,
190
191
192
193
194 "test/language/expressions/assignment/fn-name-lhs-cover.js": true,
195
196
197 "test/annexB/built-ins/RegExp/RegExp-invalid-control-escape-character-class.js": true,
198 "test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.js": true,
199
200
201 "test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js": true,
202 }
203
204 featuresBlackList = []string{
205 "async-iteration",
206 "Symbol.asyncIterator",
207 "BigInt",
208 "resizable-arraybuffer",
209 "regexp-named-groups",
210 "regexp-dotall",
211 "regexp-unicode-property-escapes",
212 "regexp-match-indices",
213 "legacy-regexp",
214 "tail-call-optimization",
215 "Temporal",
216 "import-assertions",
217 "dynamic-import",
218 "logical-assignment-operators",
219 "import.meta",
220 "Atomics",
221 "Atomics.waitAsync",
222 "FinalizationRegistry",
223 "WeakRef",
224 "numeric-separator-literal",
225 "__getter__",
226 "__setter__",
227 "ShadowRealm",
228 "SharedArrayBuffer",
229 "error-cause",
230 "decorators",
231 "regexp-v-flag",
232 }
233 )
234
235 func init() {
236
237 skip := func(prefixes ...string) {
238 for _, prefix := range prefixes {
239 skipPrefixes.Add(prefix)
240 }
241 }
242
243 skip(
244
245 "test/language/identifiers/start-unicode-14.",
246 "test/language/identifiers/part-unicode-14.",
247
248
249 "test/built-ins/Async",
250
251
252 "test/language/statements/class/elements/wrapped-in-sc-rs-static-async-generator-",
253 "test/language/statements/class/elements/same-line-method-rs-static-async-generator-",
254 "test/language/statements/class/elements/regular-definitions-rs-static-async-generator-",
255 "test/language/statements/class/elements/private-static-async-generator-",
256 "test/language/statements/class/elements/new-sc-line-method-rs-static-async-generator-",
257 "test/language/statements/class/elements/multiple-stacked-definitions-rs-static-async-generator-",
258 "test/language/statements/class/elements/new-no-sc-line-method-rs-static-async-generator-",
259 "test/language/statements/class/elements/multiple-definitions-rs-static-async-generator-",
260 "test/language/statements/class/elements/after-same-line-static-method-rs-static-async-generator-",
261 "test/language/statements/class/elements/after-same-line-method-rs-static-async-generator-",
262 "test/language/statements/class/elements/after-same-line-static-method-rs-static-async-generator-",
263
264 "test/language/expressions/class/elements/wrapped-in-sc-rs-static-async-generator-",
265 "test/language/expressions/class/elements/same-line-method-rs-static-async-generator-",
266 "test/language/expressions/class/elements/regular-definitions-rs-static-async-generator-",
267 "test/language/expressions/class/elements/private-static-async-generator-",
268 "test/language/expressions/class/elements/new-sc-line-method-rs-static-async-generator-",
269 "test/language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-generator-",
270 "test/language/expressions/class/elements/new-no-sc-line-method-rs-static-async-generator-",
271 "test/language/expressions/class/elements/multiple-definitions-rs-static-async-generator-",
272 "test/language/expressions/class/elements/after-same-line-static-method-rs-static-async-generator-",
273 "test/language/expressions/class/elements/after-same-line-method-rs-static-async-generator-",
274 "test/language/expressions/class/elements/after-same-line-static-method-rs-static-async-generator-",
275
276 "test/language/eval-code/direct/async-gen-",
277
278
279 "test/built-ins/TypedArrayConstructors/BigUint64Array/",
280 "test/built-ins/TypedArrayConstructors/BigInt64Array/",
281
282
283 "test/language/literals/regexp/u-",
284
285
286 "test/language/literals/string/legacy-octal-",
287 "test/language/literals/string/legacy-non-octal-",
288
289
290 "test/language/export/",
291 "test/language/import/",
292 "test/language/module-code/",
293 )
294
295 }
296
297 type tc39Test struct {
298 name string
299 f func(t *testing.T)
300 }
301
302 type tc39BenchmarkItem struct {
303 name string
304 duration time.Duration
305 }
306
307 type tc39BenchmarkData []tc39BenchmarkItem
308
309 type tc39TestCtx struct {
310 base string
311 t *testing.T
312 prgCache map[string]*Program
313 prgCacheLock sync.Mutex
314 enableBench bool
315 benchmark tc39BenchmarkData
316 benchLock sync.Mutex
317 sabStub *Program
318
319 testQueue []tc39Test
320 }
321
322 type TC39MetaNegative struct {
323 Phase, Type string
324 }
325
326 type tc39Meta struct {
327 Negative TC39MetaNegative
328 Includes []string
329 Flags []string
330 Features []string
331 Es5id string
332 Es6id string
333 Esid string
334 }
335
336 type prefixList struct {
337 prefixes map[int]map[string]struct{}
338 }
339
340 func (pl *prefixList) Add(prefix string) {
341 l := pl.prefixes[len(prefix)]
342 if l == nil {
343 l = make(map[string]struct{})
344 if pl.prefixes == nil {
345 pl.prefixes = make(map[int]map[string]struct{})
346 }
347 pl.prefixes[len(prefix)] = l
348 }
349 l[prefix] = struct{}{}
350 }
351
352 func (pl *prefixList) Match(s string) bool {
353 for l, prefixes := range pl.prefixes {
354 if len(s) >= l {
355 if _, exists := prefixes[s[:l]]; exists {
356 return true
357 }
358 }
359 }
360 return false
361 }
362
363 func (m *tc39Meta) hasFlag(flag string) bool {
364 for _, f := range m.Flags {
365 if f == flag {
366 return true
367 }
368 }
369 return false
370 }
371
372 func parseTC39File(name string) (*tc39Meta, string, error) {
373 f, err := os.Open(name)
374 if err != nil {
375 return nil, "", err
376 }
377 defer f.Close()
378
379 b, err := io.ReadAll(f)
380 if err != nil {
381 return nil, "", err
382 }
383
384 str := string(b)
385 metaStart := strings.Index(str, "/*---")
386 if metaStart == -1 {
387 return nil, "", invalidFormatError
388 } else {
389 metaStart += 5
390 }
391 metaEnd := strings.Index(str, "---*/")
392 if metaEnd == -1 || metaEnd <= metaStart {
393 return nil, "", invalidFormatError
394 }
395
396 var meta tc39Meta
397 err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
398 if err != nil {
399 return nil, "", err
400 }
401
402 if meta.Negative.Type != "" && meta.Negative.Phase == "" {
403 return nil, "", errors.New("negative type is set, but phase isn't")
404 }
405
406 return &meta, str, nil
407 }
408
409 func (*tc39TestCtx) detachArrayBuffer(call FunctionCall) Value {
410 if obj, ok := call.Argument(0).(*Object); ok {
411 if buf, ok := obj.self.(*arrayBufferObject); ok {
412 buf.detach()
413 return _undefined
414 }
415 }
416 panic(typeError("detachArrayBuffer() is called with incompatible argument"))
417 }
418
419 func (*tc39TestCtx) throwIgnorableTestError(FunctionCall) Value {
420 panic(ignorableTestError)
421 }
422
423 func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.TB) {
424 defer func() {
425 if x := recover(); x != nil {
426 panic(fmt.Sprintf("panic while running %s: %v", name, x))
427 }
428 }()
429 vm := New()
430 _262 := vm.NewObject()
431 _262.Set("detachArrayBuffer", ctx.detachArrayBuffer)
432 _262.Set("createRealm", ctx.throwIgnorableTestError)
433 _262.Set("evalScript", func(call FunctionCall) Value {
434 script := call.Argument(0).String()
435 result, err := vm.RunString(script)
436 if err != nil {
437 panic(err)
438 }
439 return result
440 })
441 vm.Set("$262", _262)
442 vm.Set("IgnorableTestError", ignorableTestError)
443 vm.RunProgram(ctx.sabStub)
444 var out []string
445 async := meta.hasFlag("async")
446 if async {
447 err := ctx.runFile(ctx.base, path.Join("harness", "doneprintHandle.js"), vm)
448 if err != nil {
449 t.Fatal(err)
450 }
451 vm.Set("print", func(msg string) {
452 out = append(out, msg)
453 })
454 } else {
455 vm.Set("print", t.Log)
456 }
457
458 err, early := ctx.runTC39Script(name, src, meta.Includes, vm)
459
460 if err != nil {
461 if meta.Negative.Type == "" {
462 if err, ok := err.(*Exception); ok {
463 if err.Value() == ignorableTestError {
464 t.Skip("Test threw IgnorableTestError")
465 }
466 }
467 t.Fatalf("%s: %v", name, err)
468 } else {
469 if (meta.Negative.Phase == "early" || meta.Negative.Phase == "parse") && !early || meta.Negative.Phase == "runtime" && early {
470 t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
471 }
472 var errType string
473
474 switch err := err.(type) {
475 case *Exception:
476 if o, ok := err.Value().(*Object); ok {
477 if c := o.Get("constructor"); c != nil {
478 if c, ok := c.(*Object); ok {
479 errType = c.Get("name").String()
480 } else {
481 t.Fatalf("%s: error constructor is not an object (%v)", name, o)
482 }
483 } else {
484 t.Fatalf("%s: error does not have a constructor (%v)", name, o)
485 }
486 } else {
487 t.Fatalf("%s: error is not an object (%v)", name, err.Value())
488 }
489 case *CompilerSyntaxError:
490 errType = "SyntaxError"
491 case *CompilerReferenceError:
492 errType = "ReferenceError"
493 default:
494 t.Fatalf("%s: error is not a JS error: %v", name, err)
495 }
496
497 if errType != meta.Negative.Type {
498 vm.vm.prg.dumpCode(t.Logf)
499 t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
500 }
501 }
502 } else {
503 if meta.Negative.Type != "" {
504 vm.vm.prg.dumpCode(t.Logf)
505 t.Fatalf("%s: Expected error: %v", name, err)
506 }
507 }
508
509 if vm.vm.sp != 0 {
510 t.Fatalf("sp: %d", vm.vm.sp)
511 }
512
513 if l := len(vm.vm.iterStack); l > 0 {
514 t.Fatalf("iter stack is not empty: %d", l)
515 }
516 if async {
517 complete := false
518 for _, line := range out {
519 if strings.HasPrefix(line, "Test262:AsyncTestFailure:") {
520 t.Fatal(line)
521 } else if line == "Test262:AsyncTestComplete" {
522 complete = true
523 }
524 }
525 if !complete {
526 for _, line := range out {
527 t.Log(line)
528 }
529 t.Fatal("Test262:AsyncTestComplete was not printed")
530 }
531 }
532 }
533
534 func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
535 if skipList[name] {
536 t.Skip("Excluded")
537 }
538 if skipPrefixes.Match(name) {
539 t.Skip("Excluded")
540 }
541 p := path.Join(ctx.base, name)
542 meta, src, err := parseTC39File(p)
543 if err != nil {
544
545 t.Errorf("Could not parse %s: %v", name, err)
546 return
547 }
548 if meta.hasFlag("module") {
549 t.Skip("module")
550 }
551 if meta.Es5id == "" {
552 for _, feature := range meta.Features {
553 for _, bl := range featuresBlackList {
554 if feature == bl {
555 t.Skip("Blacklisted feature")
556 }
557 }
558 }
559 }
560
561 var startTime time.Time
562 if ctx.enableBench {
563 startTime = time.Now()
564 }
565
566 hasRaw := meta.hasFlag("raw")
567
568 if hasRaw || !meta.hasFlag("onlyStrict") {
569
570 t.Logf("Running normal test: %s", name)
571 ctx.runTC39Test(name, src, meta, t)
572 }
573
574 if !hasRaw && !meta.hasFlag("noStrict") {
575
576 t.Logf("Running strict test: %s", name)
577 ctx.runTC39Test(name, "'use strict';\n"+src, meta, t)
578 }
579
580 if ctx.enableBench {
581 ctx.benchLock.Lock()
582 ctx.benchmark = append(ctx.benchmark, tc39BenchmarkItem{
583 name: name,
584 duration: time.Since(startTime),
585 })
586 ctx.benchLock.Unlock()
587 }
588
589 }
590
591 func (ctx *tc39TestCtx) init() {
592 ctx.prgCache = make(map[string]*Program)
593 ctx.sabStub = MustCompile("sabStub.js", `
594 Object.defineProperty(this, "SharedArrayBuffer", {
595 get: function() {
596 throw IgnorableTestError;
597 }
598 });`,
599 false)
600 }
601
602 func (ctx *tc39TestCtx) compile(base, name string) (*Program, error) {
603 ctx.prgCacheLock.Lock()
604 defer ctx.prgCacheLock.Unlock()
605
606 prg := ctx.prgCache[name]
607 if prg == nil {
608 fname := path.Join(base, name)
609 f, err := os.Open(fname)
610 if err != nil {
611 return nil, err
612 }
613 defer f.Close()
614
615 b, err := io.ReadAll(f)
616 if err != nil {
617 return nil, err
618 }
619
620 str := string(b)
621 prg, err = Compile(name, str, false)
622 if err != nil {
623 return nil, err
624 }
625 ctx.prgCache[name] = prg
626 }
627
628 return prg, nil
629 }
630
631 func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
632 prg, err := ctx.compile(base, name)
633 if err != nil {
634 return err
635 }
636 _, err = vm.RunProgram(prg)
637 return err
638 }
639
640 func (ctx *tc39TestCtx) runTC39Script(name, src string, includes []string, vm *Runtime) (err error, early bool) {
641 early = true
642 err = ctx.runFile(ctx.base, path.Join("harness", "assert.js"), vm)
643 if err != nil {
644 return
645 }
646
647 err = ctx.runFile(ctx.base, path.Join("harness", "sta.js"), vm)
648 if err != nil {
649 return
650 }
651
652 for _, include := range includes {
653 err = ctx.runFile(ctx.base, path.Join("harness", include), vm)
654 if err != nil {
655 return
656 }
657 }
658
659 var p *Program
660 p, err = Compile(name, src, false)
661
662 if err != nil {
663 return
664 }
665
666 early = false
667 _, err = vm.RunProgram(p)
668
669 return
670 }
671
672 func (ctx *tc39TestCtx) runTC39Tests(name string) {
673 files, err := os.ReadDir(path.Join(ctx.base, name))
674 if err != nil {
675 ctx.t.Fatal(err)
676 }
677
678 for _, file := range files {
679 if file.Name()[0] == '.' {
680 continue
681 }
682 if file.IsDir() {
683 ctx.runTC39Tests(path.Join(name, file.Name()))
684 } else {
685 fileName := file.Name()
686 if strings.HasSuffix(fileName, ".js") && !strings.HasSuffix(fileName, "_FIXTURE.js") {
687 name := path.Join(name, fileName)
688 ctx.runTest(name, func(t *testing.T) {
689 ctx.runTC39File(name, t)
690 })
691 }
692 }
693 }
694
695 }
696
697 func TestTC39(t *testing.T) {
698 if testing.Short() {
699 t.Skip()
700 }
701
702 if _, err := os.Stat(tc39BASE); err != nil {
703 t.Skipf("If you want to run tc39 tests, download them from https://github.com/tc39/test262 and put into %s. See .tc39_test262_checkout.sh for the latest working commit id. (%v)", tc39BASE, err)
704 }
705
706 ctx := &tc39TestCtx{
707 base: tc39BASE,
708 }
709 ctx.init()
710
711
712 t.Run("tc39", func(t *testing.T) {
713 ctx.t = t
714
715 ctx.runTC39Tests("test/language")
716 ctx.runTC39Tests("test/built-ins")
717 ctx.runTC39Tests("test/annexB/built-ins/String/prototype/substr")
718 ctx.runTC39Tests("test/annexB/built-ins/String/prototype/trimLeft")
719 ctx.runTC39Tests("test/annexB/built-ins/String/prototype/trimRight")
720 ctx.runTC39Tests("test/annexB/built-ins/escape")
721 ctx.runTC39Tests("test/annexB/built-ins/unescape")
722 ctx.runTC39Tests("test/annexB/built-ins/RegExp")
723
724 ctx.flush()
725 })
726
727 if ctx.enableBench {
728 sort.Slice(ctx.benchmark, func(i, j int) bool {
729 return ctx.benchmark[i].duration > ctx.benchmark[j].duration
730 })
731 bench := ctx.benchmark
732 if len(bench) > 50 {
733 bench = bench[:50]
734 }
735 for _, item := range bench {
736 fmt.Printf("%s\t%d\n", item.name, item.duration/time.Millisecond)
737 }
738 }
739 }
740
View as plain text