github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/tc39_test.go (about) 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 // out-of-date (https://github.com/tc39/test262/issues/3407) 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 // timezone 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 // floating point date calculations 87 "test/built-ins/Date/UTC/fp-evaluation-order.js": true, 88 89 // quantifier integer limit in regexp 90 "test/built-ins/RegExp/quantifier-integer-limit.js": true, 91 92 // GetFunctionRealm 93 "test/built-ins/Function/internals/Construct/base-ctor-revoked-proxy.js": true, 94 95 // Uses deprecated __lookupGetter__/__lookupSetter__ 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 // restricted unicode regexp syntax 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 // Because goja parser works in UTF-8 it is not possible to pass strings containing invalid UTF-16 code points. 116 // This is mitigated by escaping them as \uXXXX, however because of this the RegExp source becomes 117 // `\uXXXX` instead of `<the actual UTF-16 code point of XXXX>`. 118 // The resulting RegExp will work exactly the same, but it causes these two tests to fail. 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 // async generator 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 // async iterator 163 "test/language/expressions/optional-chaining/iteration-statement-for-await-of.js": true, 164 165 // legacy number literals 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 // integer separators 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 // BigInt 182 "test/built-ins/Object/seal/seal-biguint64array.js": true, 183 "test/built-ins/Object/seal/seal-bigint64array.js": true, 184 185 // Regexp 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 // FIXME bugs 192 193 // Left-hand side as a CoverParenthesizedExpression 194 "test/language/expressions/assignment/fn-name-lhs-cover.js": true, 195 196 // Character \ missing from character class [\c] 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 // Skip due to regexp named groups 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 // Go 1.16 only supports unicode 13 245 "test/language/identifiers/start-unicode-14.", 246 "test/language/identifiers/part-unicode-14.", 247 248 // generators and async generators (harness/hidden-constructors.js) 249 "test/built-ins/Async", 250 251 // async generators 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 // BigInt 279 "test/built-ins/TypedArrayConstructors/BigUint64Array/", 280 "test/built-ins/TypedArrayConstructors/BigInt64Array/", 281 282 // restricted unicode regexp syntax 283 "test/language/literals/regexp/u-", 284 285 // legacy octal escape in strings in strict mode 286 "test/language/literals/string/legacy-octal-", 287 "test/language/literals/string/legacy-non-octal-", 288 289 // modules 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 //lint:ignore U1000 Only used with race 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 //t.Fatalf("Could not parse %s: %v", name, err) 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 //log.Printf("Running normal test: %s", name) 570 t.Logf("Running normal test: %s", name) 571 ctx.runTC39Test(name, src, meta, t) 572 } 573 574 if !hasRaw && !meta.hasFlag("noStrict") { 575 //log.Printf("Running strict test: %s", name) 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 //ctx.enableBench = true 711 712 t.Run("tc39", func(t *testing.T) { 713 ctx.t = t 714 //ctx.runTC39File("test/language/types/number/8.5.1.js", t) 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 }