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