diff --git a/.gitignore b/.gitignore index 08fb1b04..a3003258 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,7 @@ v8_build /project-template-ios/.build_env_vars.sh /project-template-ios/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/ /project-template-vision/.build_env_vars.sh -/project-template-vision/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist \ No newline at end of file +/project-template-vision/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist + +# test262 +dist-test/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index fa45c83c..9aaa9e62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = libffi url = https://github.com/NativeScript/libffi.git branch = darind/v8-ios +[submodule "TestRunner/app/tests/vendor/test262"] + path = TestRunner/app/tests/vendor/test262 + url = https://github.com/tc39/test262.git diff --git a/NativeScript/inspector/JsV8InspectorClient.mm b/NativeScript/inspector/JsV8InspectorClient.mm index fafe0c68..60210f56 100644 --- a/NativeScript/inspector/JsV8InspectorClient.mm +++ b/NativeScript/inspector/JsV8InspectorClient.mm @@ -22,6 +22,13 @@ namespace v8_inspector { +namespace { +StringView Make8BitStringView(const std::string& value) { + return StringView(reinterpret_cast(value.data()), + value.size()); +} +} // namespace + #define NOTIFICATION(name) \ [[NSString stringWithFormat:@"%@:NativeScript.Debug.%s", \ [[NSBundle mainBundle] bundleIdentifier], name] UTF8String] @@ -257,8 +264,7 @@ } void JsV8InspectorClient::dispatchMessage(const std::string& message) { - std::vector vector = tns::ToVector(message); - StringView messageView(vector.data(), vector.size()); + StringView messageView = Make8BitStringView(message); Isolate* isolate = isolate_; v8::Locker locker(isolate); Isolate::Scope isolate_scope(isolate); @@ -343,8 +349,7 @@ auto returnString = GetReturnMessageFromDomainHandlerResult(result, requestId); if (returnString.size() > 0) { - std::vector vector = tns::ToVector(returnString); - StringView messageView(vector.data(), vector.size()); + StringView messageView = Make8BitStringView(returnString); auto msg = StringBuffer::create(messageView); this->sendNotification(std::move(msg)); } @@ -486,8 +491,7 @@ Local arg = args[0].As(); std::string message = tns::ToString(isolate, arg); - std::vector vector = tns::ToVector(message); - StringView messageView(vector.data(), vector.size()); + StringView messageView = Make8BitStringView(message); auto msg = StringBuffer::create(messageView); client->sendNotification(std::move(msg)); } diff --git a/NativeScript/inspector/src/inspector/string-16.h b/NativeScript/inspector/src/inspector/string-16.h index 7dfc5e34..ac9892f5 100644 --- a/NativeScript/inspector/src/inspector/string-16.h +++ b/NativeScript/inspector/src/inspector/string-16.h @@ -17,7 +17,7 @@ namespace v8_inspector { -using UChar = uint16_t; +using UChar = char16_t; class String16 { public: diff --git a/NativeScript/inspector/src/inspector/string-util.h b/NativeScript/inspector/src/inspector/string-util.h index 7f427d9b..82055588 100644 --- a/NativeScript/inspector/src/inspector/string-util.h +++ b/NativeScript/inspector/src/inspector/string-util.h @@ -30,13 +30,13 @@ class StringUtil { } static String fromUTF16LE(const uint16_t* data, size_t length) { - return String16::fromUTF16LE(data, length); + return String16::fromUTF16LE(reinterpret_cast(data), length); } static const uint8_t* CharactersLatin1(const String& s) { return nullptr; } static const uint8_t* CharactersUTF8(const String& s) { return nullptr; } static const uint16_t* CharactersUTF16(const String& s) { - return s.characters16(); + return reinterpret_cast(s.characters16()); } static size_t CharacterCount(const String& s) { return s.length(); } }; diff --git a/NativeScript/inspector/utils.mm b/NativeScript/inspector/utils.mm index d078e525..63a1f898 100644 --- a/NativeScript/inspector/utils.mm +++ b/NativeScript/inspector/utils.mm @@ -35,17 +35,16 @@ } std::string v8_inspector::ToStdString(const StringView& value) { - std::vector buffer(value.length()); + std::u16string value16; + value16.resize(value.length()); for (size_t i = 0; i < value.length(); i++) { if (value.is8Bit()) { - buffer[i] = value.characters8()[i]; + value16[i] = static_cast(value.characters8()[i]); } else { - buffer[i] = value.characters16()[i]; + value16[i] = static_cast(value.characters16()[i]); } } - std::u16string value16(buffer.begin(), buffer.end()); - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // FIXME: std::codecvt_utf8_utf16 is deprecated std::wstring_convert, char16_t> convert; diff --git a/NativeScript/v8runtime/V8Runtime.cpp b/NativeScript/v8runtime/V8Runtime.cpp index 42355bdf..2d49a34d 100644 --- a/NativeScript/v8runtime/V8Runtime.cpp +++ b/NativeScript/v8runtime/V8Runtime.cpp @@ -431,7 +431,7 @@ std::shared_ptr V8Runtime::prepareJavaScript( jsi::Value V8Runtime::evaluatePreparedJavaScript( const std::shared_ptr& js) { - return evaluateJavaScript(nullptr, nullptr); + return evaluateJavaScript(nullptr, ""); } void V8Runtime::queueMicrotask(const jsi::Function& callback) { @@ -1586,4 +1586,4 @@ void V8Runtime::OnHostFuncionContainerCallback( } } -} // namespace rnv8 \ No newline at end of file +} // namespace rnv8 diff --git a/README.md b/README.md index 7c396fc6..b0c4aaca 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,42 @@ Runtime initialization took 55ms If all tests pass, everything is good! At this point you can make changes to the runtime, add breakpoints and step through with the debugger. In the next section we'll see how to attach the runtime to an existing NativeScript application allowing us to debug runtime issues in actual apps. +## Test262 integration + +The `TestRunner` target can host a filtered subset of [Test262](https://github.com/tc39/test262) inside the same Jasmine and JUnit pipeline that the existing runtime tests already use. + +The integration in this repo is intentionally phased: + +1. Test262 cases are discovered ahead of time and written to `TestRunner/app/tests/Test262/generated-manifest.json`. +2. Each discovered case is executed in its own `Worker`, so it gets an isolated realm instead of sharing Jasmine's main global object. +3. The generated specs are reported through the existing `TestRunner` JUnit output, so they show up in the current Xcode and CI flow. + +To wire in the upstream suite, add it as a submodule under the bundled test tree: + +```bash +git submodule add https://github.com/tc39/test262.git TestRunner/app/tests/vendor/test262 +git submodule update --init --recursive +``` + +Then enable and tune `TestRunner/app/tests/Test262/config.js`. The TestRunner build now regenerates the manifest automatically by running: + +```bash +node ./scripts/generate-test262-manifest.js +``` + +By default, enabling the suite does not try to run the whole upstream corpus. It starts from the curated prefixes in `TestRunner/app/tests/Test262/curated-prefixes.json` and caps generation at 250 entries unless you override it. + +Useful environment variables for local runs and CI sharding: + +```bash +TEST262_FILTER=built-ins/Array +TEST262_LIMIT=250 +TEST262_SHARD_COUNT=4 +TEST262_SHARD_INDEX=0 +``` + +The current adapter is deliberately conservative. It skips unsupported `module` and `raw` cases by default and is disabled by default so existing CI remains unchanged until you choose a supported subset. + # Attaching the runtime to a NativeScript app In the existing app, we need to prepare the Xcode project using `ns prepare ios`. This will create a folder named `platforms/ios` and in there a `.xcworkspace` (or .xcodeproject but note the following...). diff --git a/TestRunner/app/tests/Test262/RunnerWorker.js b/TestRunner/app/tests/Test262/RunnerWorker.js new file mode 100644 index 00000000..59d4c22d --- /dev/null +++ b/TestRunner/app/tests/Test262/RunnerWorker.js @@ -0,0 +1,184 @@ +function serializeError(error) { + if (!error) { + return "Unknown error"; + } + + if (typeof error === "string") { + return error; + } + + var parts = []; + if (error.name) { + parts.push(error.name); + } + if (error.message) { + parts.push(error.message); + } + + var summary = parts.join(": "); + if (error.stack) { + summary += "\n" + error.stack; + } + + return summary || String(error); +} + +function readTextFile(path) { + var data = NSData.dataWithContentsOfFile(path); + if (!data) { + throw new Error("Unable to read file: " + path); + } + + var text = NSString.alloc().initWithDataEncoding(data, NSUTF8StringEncoding); + if (!text) { + throw new Error("Unable to decode UTF-8 file: " + path); + } + + return text.toString(); +} + +function withSourceURL(source, path) { + return source + "\n//# sourceURL=" + path.replace(/\\/g, "/"); +} + +function evalGlobal(source, path) { + var globalEval = (0, eval); + return globalEval(withSourceURL(source, path)); +} + +function compileScript(source, path) { + return Function(withSourceURL(source, path)); +} + +function expectedErrorMatches(error, negative) { + if (!negative || !negative.type) { + return true; + } + + return error && (error.name === negative.type || (error.constructor && error.constructor.name === negative.type)); +} + +function makePrelude() { + globalThis.$262 = { + global: globalThis + }; + + globalThis.print = function () { + }; + + globalThis.reportCompare = function (expected, actual, message) { + if (expected !== actual) { + throw new Error(message || ("Expected " + expected + " but received " + actual)); + } + }; +} + +function loadHarness(test262Root, includes) { + includes.forEach(function (includeName) { + var harnessPath = test262Root + "/harness/" + includeName; + evalGlobal(readTextFile(harnessPath), harnessPath); + }); +} + +function postPass() { + postMessage({ status: "passed" }); +} + +function postFailure(error) { + postMessage({ + status: "failed", + error: serializeError(error) + }); +} + +function runCase(payload) { + var settled = false; + var test262Root = NSBundle.mainBundle.bundlePath + "/app/tests/" + payload.bundleSubmodulePath; + var testPath = test262Root + "/test/" + payload.relativePath; + var source = readTextFile(testPath); + var wrappedSource = payload.mode === "strict" ? "\"use strict\";\n" + source : source; + + function completeWithPass() { + if (settled) { + return; + } + + settled = true; + postPass(); + } + + function completeWithFailure(error) { + if (settled) { + return; + } + + settled = true; + postFailure(error); + } + + makePrelude(); + + if (payload.async) { + globalThis.$DONE = function (error) { + if (error === undefined || error === null) { + completeWithPass(); + return; + } + + completeWithFailure(error); + }; + } + + loadHarness(test262Root, payload.includes || []); + + if (payload.negative && payload.negative.phase === "parse") { + try { + compileScript(wrappedSource, testPath); + } catch (error) { + if (expectedErrorMatches(error, payload.negative)) { + completeWithPass(); + return; + } + + completeWithFailure(error); + return; + } + + completeWithFailure(new Error("Expected parse failure but compilation succeeded")); + return; + } + + try { + evalGlobal(wrappedSource, testPath); + } catch (error) { + if (payload.negative) { + if (expectedErrorMatches(error, payload.negative)) { + completeWithPass(); + return; + } + + completeWithFailure(error); + return; + } + + completeWithFailure(error); + return; + } + + if (payload.negative) { + completeWithFailure(new Error("Expected runtime failure but test completed successfully")); + return; + } + + if (!payload.async) { + completeWithPass(); + } +} + +onmessage = function (message) { + try { + runCase(message.data); + } catch (error) { + postFailure(error); + } +}; \ No newline at end of file diff --git a/TestRunner/app/tests/Test262/config.js b/TestRunner/app/tests/Test262/config.js new file mode 100644 index 00000000..81d0047a --- /dev/null +++ b/TestRunner/app/tests/Test262/config.js @@ -0,0 +1,28 @@ +module.exports = { + enabled: true, + suiteName: "Test262", + projectSubmodulePath: "TestRunner/app/tests/vendor/test262", + bundleSubmodulePath: "vendor/test262", + generatedManifestPath: "TestRunner/app/tests/Test262/generated-manifest.json", + curatedIncludePathsPath: "TestRunner/app/tests/Test262/curated-prefixes.json", + harnessDefaults: ["assert.js", "sta.js"], + unsupportedFlags: ["module", "raw"], + unsupportedIncludes: [ + "agent.js", + "detachArrayBuffer.js", + "nondeterministic.js", + "shadowrealm.js", + "timer.js", + "workerHelper.js" + ], + unsupportedFeatures: [ + "Array.fromAsync", + "cross-realm", + "ShadowRealm" + ], + includePaths: ["built-ins/Object"], + excludePaths: ["built-ins/Array/from/elements-deleted-after.js"], + timeoutMs: 4000, + defaultLimit: 100, + expandStrictVariants: true +}; \ No newline at end of file diff --git a/TestRunner/app/tests/Test262/curated-prefixes.json b/TestRunner/app/tests/Test262/curated-prefixes.json new file mode 100644 index 00000000..66d68c5f --- /dev/null +++ b/TestRunner/app/tests/Test262/curated-prefixes.json @@ -0,0 +1,48 @@ +[ + "built-ins/Array", + "built-ins/Boolean", + "built-ins/Date", + "built-ins/Error", + "built-ins/Function", + "built-ins/JSON", + "built-ins/Map", + "built-ins/Math", + "built-ins/Number", + "built-ins/Object", + "built-ins/Promise", + "built-ins/Reflect", + "built-ins/RegExp", + "built-ins/Set", + "built-ins/String", + "built-ins/Symbol", + "built-ins/globalThis", + "built-ins/parseFloat", + "built-ins/parseInt", + "language/expressions", + "language/function-code", + "language/global-code", + "language/identifiers", + "language/literals", + "language/line-terminators", + "language/statements/block", + "language/statements/break", + "language/statements/class", + "language/statements/const", + "language/statements/continue", + "language/statements/debugger", + "language/statements/do-while", + "language/statements/empty", + "language/statements/expression", + "language/statements/for", + "language/statements/for-in", + "language/statements/for-of", + "language/statements/function", + "language/statements/if", + "language/statements/let", + "language/statements/return", + "language/statements/switch", + "language/statements/throw", + "language/statements/try", + "language/statements/variable", + "language/statements/while" +] \ No newline at end of file diff --git a/TestRunner/app/tests/Test262/generated-manifest.json b/TestRunner/app/tests/Test262/generated-manifest.json new file mode 100644 index 00000000..28618fdc --- /dev/null +++ b/TestRunner/app/tests/Test262/generated-manifest.json @@ -0,0 +1,1184 @@ +{ + "generatedAt": "2026-03-28T18:11:35.062Z", + "enabled": true, + "reason": "generated", + "summary": { + "discovered": 53125, + "included": 100, + "excluded": 46325, + "exclusions": { + "not-included": 49713, + "excluded-path": 1, + "unsupported-feature": 1 + }, + "shardCount": 0, + "shardIndex": 0, + "filter": [], + "includePrefixes": [ + "built-ins/Object" + ], + "curatedIncludePrefixes": [ + "built-ins/Array", + "built-ins/Boolean", + "built-ins/Date", + "built-ins/Error", + "built-ins/Function", + "built-ins/JSON", + "built-ins/Map", + "built-ins/Math", + "built-ins/Number", + "built-ins/Object", + "built-ins/Promise", + "built-ins/Reflect", + "built-ins/RegExp", + "built-ins/Set", + "built-ins/String", + "built-ins/Symbol", + "built-ins/globalThis", + "built-ins/parseFloat", + "built-ins/parseInt", + "language/expressions", + "language/function-code", + "language/global-code", + "language/identifiers", + "language/literals", + "language/line-terminators", + "language/statements/block", + "language/statements/break", + "language/statements/class", + "language/statements/const", + "language/statements/continue", + "language/statements/debugger", + "language/statements/do-while", + "language/statements/empty", + "language/statements/expression", + "language/statements/for", + "language/statements/for-in", + "language/statements/for-of", + "language/statements/function", + "language/statements/if", + "language/statements/let", + "language/statements/return", + "language/statements/switch", + "language/statements/throw", + "language/statements/try", + "language/statements/variable", + "language/statements/while" + ], + "limit": 100 + }, + "tests": [ + { + "id": "built-ins/Object/assign/assign-descriptor.js [non-strict]", + "relativePath": "built-ins/Object/assign/assign-descriptor.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/assign-descriptor.js [strict]", + "relativePath": "built-ins/Object/assign/assign-descriptor.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/assign-length.js [non-strict]", + "relativePath": "built-ins/Object/assign/assign-length.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/assign-length.js [strict]", + "relativePath": "built-ins/Object/assign/assign-length.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js [non-strict]", + "relativePath": "built-ins/Object/assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js [strict]", + "relativePath": "built-ins/Object/assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/invoked-as-ctor.js [non-strict]", + "relativePath": "built-ins/Object/assign/invoked-as-ctor.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/invoked-as-ctor.js [strict]", + "relativePath": "built-ins/Object/assign/invoked-as-ctor.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/name.js [non-strict]", + "relativePath": "built-ins/Object/assign/name.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/name.js [strict]", + "relativePath": "built-ins/Object/assign/name.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "propertyHelper.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/not-a-constructor.js [non-strict]", + "relativePath": "built-ins/Object/assign/not-a-constructor.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "isConstructor.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/not-a-constructor.js [strict]", + "relativePath": "built-ins/Object/assign/not-a-constructor.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "isConstructor.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/ObjectOverride-sameproperty.js [non-strict]", + "relativePath": "built-ins/Object/assign/ObjectOverride-sameproperty.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/ObjectOverride-sameproperty.js [strict]", + "relativePath": "built-ins/Object/assign/ObjectOverride-sameproperty.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/OnlyOneArgument.js [non-strict]", + "relativePath": "built-ins/Object/assign/OnlyOneArgument.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/OnlyOneArgument.js [strict]", + "relativePath": "built-ins/Object/assign/OnlyOneArgument.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Override-notstringtarget.js [non-strict]", + "relativePath": "built-ins/Object/assign/Override-notstringtarget.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Override-notstringtarget.js [strict]", + "relativePath": "built-ins/Object/assign/Override-notstringtarget.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Override.js [non-strict]", + "relativePath": "built-ins/Object/assign/Override.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Override.js [strict]", + "relativePath": "built-ins/Object/assign/Override.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-get-attr-error.js [non-strict]", + "relativePath": "built-ins/Object/assign/source-get-attr-error.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-get-attr-error.js [strict]", + "relativePath": "built-ins/Object/assign/source-get-attr-error.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-non-enum.js [non-strict]", + "relativePath": "built-ins/Object/assign/source-non-enum.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-non-enum.js [strict]", + "relativePath": "built-ins/Object/assign/source-non-enum.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-Null-Undefined.js [non-strict]", + "relativePath": "built-ins/Object/assign/Source-Null-Undefined.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-Null-Undefined.js [strict]", + "relativePath": "built-ins/Object/assign/Source-Null-Undefined.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-Number-Boolen-Symbol.js [non-strict]", + "relativePath": "built-ins/Object/assign/Source-Number-Boolen-Symbol.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-Number-Boolen-Symbol.js [strict]", + "relativePath": "built-ins/Object/assign/Source-Number-Boolen-Symbol.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-desc-missing.js [non-strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-desc-missing.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-desc-missing.js [strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-desc-missing.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-error.js [non-strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-error.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-error.js [strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-error.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-keys-error.js [non-strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-keys-error.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/source-own-prop-keys-error.js [strict]", + "relativePath": "built-ins/Object/assign/source-own-prop-keys-error.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-String.js [non-strict]", + "relativePath": "built-ins/Object/assign/Source-String.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Source-String.js [strict]", + "relativePath": "built-ins/Object/assign/Source-String.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/strings-and-symbol-order-proxy.js [non-strict]", + "relativePath": "built-ins/Object/assign/strings-and-symbol-order-proxy.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "compareArray.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/strings-and-symbol-order-proxy.js [strict]", + "relativePath": "built-ins/Object/assign/strings-and-symbol-order-proxy.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "compareArray.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/strings-and-symbol-order.js [non-strict]", + "relativePath": "built-ins/Object/assign/strings-and-symbol-order.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "compareArray.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/strings-and-symbol-order.js [strict]", + "relativePath": "built-ins/Object/assign/strings-and-symbol-order.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js", + "compareArray.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-Array.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-Array.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-Array.js [strict]", + "relativePath": "built-ins/Object/assign/target-Array.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Boolean.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Boolean.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Boolean.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Boolean.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-frozen-accessor-property-set-succeeds.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-frozen-accessor-property-set-succeeds.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-frozen-accessor-property-set-succeeds.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-frozen-accessor-property-set-succeeds.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-frozen-data-property-set-throws.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-frozen-data-property-set-throws.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-frozen-data-property-set-throws.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-frozen-data-property-set-throws.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-existing-accessor-property.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-existing-accessor-property.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-existing-accessor-property.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-existing-accessor-property.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-existing-data-property.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-existing-data-property.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-existing-data-property.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-existing-data-property.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-property-creation-throws.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-property-creation-throws.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-non-extensible-property-creation-throws.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-non-extensible-property-creation-throws.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-existing-accessor-property.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-existing-accessor-property.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-existing-accessor-property.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-existing-accessor-property.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-existing-data-property.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-existing-data-property.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-existing-data-property.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-existing-data-property.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-property-creation-throws.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-property-creation-throws.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-is-sealed-property-creation-throws.js [strict]", + "relativePath": "built-ins/Object/assign/target-is-sealed-property-creation-throws.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Null.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Null.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Null.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Null.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Number.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Number.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Number.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Number.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Object.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Object.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Object.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Object.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-set-not-writable.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-set-not-writable.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-set-not-writable.js [strict]", + "relativePath": "built-ins/Object/assign/target-set-not-writable.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-set-user-error.js [non-strict]", + "relativePath": "built-ins/Object/assign/target-set-user-error.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/target-set-user-error.js [strict]", + "relativePath": "built-ins/Object/assign/target-set-user-error.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-String.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-String.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-String.js [strict]", + "relativePath": "built-ins/Object/assign/Target-String.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Symbol.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Symbol.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Symbol.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Symbol.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Undefined.js [non-strict]", + "relativePath": "built-ins/Object/assign/Target-Undefined.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/assign/Target-Undefined.js [strict]", + "relativePath": "built-ins/Object/assign/Target-Undefined.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/bigint.js [non-strict]", + "relativePath": "built-ins/Object/bigint.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/bigint.js [strict]", + "relativePath": "built-ins/Object/bigint.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-0-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-0-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-0-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-0-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-0-2.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-0-2.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-0-2.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-0-2.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-2.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-2.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-2.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-2.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-3.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-3.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-3.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-3.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-4.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-4.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1-4.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1-4.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-2-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-2-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-2-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-2-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-2-2.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-2-2.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-2-2.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-2-2.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-3-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-3-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-3-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-3-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-4-1.js [non-strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-4-1.js", + "mode": "non-strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + }, + { + "id": "built-ins/Object/create/15.2.3.5-4-1.js [strict]", + "relativePath": "built-ins/Object/create/15.2.3.5-4-1.js", + "mode": "strict", + "async": false, + "includes": [ + "assert.js", + "sta.js" + ], + "negative": null + } + ] +} diff --git a/TestRunner/app/tests/Test262/index.js b/TestRunner/app/tests/Test262/index.js new file mode 100644 index 00000000..ef5c891f --- /dev/null +++ b/TestRunner/app/tests/Test262/index.js @@ -0,0 +1,156 @@ +var config = require("./config"); +var manifest = require("./generated-manifest.json"); +var test262ReporterInstalled = false; + +function stringifyFailure(error) { + if (!error) { + return "Unknown Test262 failure"; + } + + if (typeof error === "string") { + return error; + } + + return error.stack || error.message || String(error); +} + +function failSpec(done, error) { + expect(stringifyFailure(error)).toBeUndefined(); + done(); +} + +function installTest262Reporter() { + if (test262ReporterInstalled || !global.jasmine || typeof global.jasmine.getEnv !== "function") { + return; + } + + test262ReporterInstalled = true; + + var total = 0; + var failed = 0; + var skipped = 0; + var suitePrefix = config.suiteName + " "; + + global.jasmine.getEnv().addReporter({ + specDone: function (spec) { + if (!spec || typeof spec.fullName !== "string" || spec.fullName.indexOf(suitePrefix) !== 0) { + return; + } + + total++; + + if (spec.status === "failed") { + failed++; + } else if (spec.status === "pending") { + skipped++; + } + }, + jasmineDone: function () { + if (!total) { + console.log("TEST262: 0 specs, 0 failures, 0 skipped, 0 passed."); + return; + } + + var passed = total - failed - skipped; + console.log( + "TEST262: " + total + " specs, " + failed + " failures, " + skipped + " skipped, " + passed + " passed." + ); + } + }); +} + +function runTest262Case(testCase, done) { + var worker = new Worker("./tests/Test262/RunnerWorker.js"); + var timeout = null; + var finished = false; + + function finalize(callback) { + if (finished) { + return; + } + + finished = true; + + if (timeout !== null) { + clearTimeout(timeout); + } + + try { + worker.terminate(); + } catch (e) { + } + + callback(); + } + + timeout = setTimeout(function () { + finalize(function () { + failSpec(done, "Timed out after " + (testCase.timeoutMs || config.timeoutMs) + "ms"); + }); + }, testCase.timeoutMs || config.timeoutMs); + + worker.onerror = function (error) { + finalize(function () { + failSpec(done, error); + }); + + return true; + }; + + worker.onmessage = function (message) { + var result = message.data || {}; + + if (result.status === "passed") { + finalize(function () { + done(); + }); + return; + } + + finalize(function () { + failSpec(done, result.error || "Unknown Test262 failure"); + }); + }; + + worker.postMessage({ + bundleSubmodulePath: config.bundleSubmodulePath, + relativePath: testCase.relativePath, + mode: testCase.mode, + async: !!testCase.async, + includes: testCase.includes || [], + negative: testCase.negative || null + }); +} + +if (!config.enabled) { + module.exports = {}; + return; +} + +installTest262Reporter(); + +describe(config.suiteName, function () { + var originalTimeout; + + beforeEach(function () { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = config.timeoutMs + 1000; + }); + + afterEach(function () { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); + + if (!manifest.tests || !manifest.tests.length) { + it("has no generated manifest entries", function () { + pending("Test262 manifest is empty: " + (manifest.reason || "unknown reason")); + }); + return; + } + + manifest.tests.forEach(function (testCase) { + it(testCase.id, function (done) { + runTest262Case(testCase, done); + }); + }); +}); \ No newline at end of file diff --git a/TestRunner/app/tests/index.js b/TestRunner/app/tests/index.js index ec4384a6..b0014e4e 100644 --- a/TestRunner/app/tests/index.js +++ b/TestRunner/app/tests/index.js @@ -99,6 +99,9 @@ require("./ExceptionHandlingTests"); // Tests common for all runtimes. require("./shared/index").runAllTests(); +// Optional Test262 integration. Disabled by default and enabled via Test262/config.js. +require("./Test262"); + // (Optional) Custom testing for various optional sdk's and frameworks // These can be turned on manually to verify if needed anytime //require("./sdks/MusicKit"); diff --git a/download_llvm.sh b/download_llvm.sh index 568d7720..81251087 100755 --- a/download_llvm.sh +++ b/download_llvm.sh @@ -2,7 +2,7 @@ set -e source "$(dirname "$0")/build_utils.sh" -LLVM_VERSION="17.0.6" +LLVM_VERSION="17.0.7" function download_llvm() { checkpoint "Downloading llvm (version $LLVM_VERSION)..." diff --git a/metadata-generator/CMakeLists.txt b/metadata-generator/CMakeLists.txt index a8974978..e46d29b0 100644 --- a/metadata-generator/CMakeLists.txt +++ b/metadata-generator/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.20) project(MetadataGenerator) include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) #set(CMAKE_VERBOSE_MAKEFILE ON) @@ -13,7 +14,7 @@ if (NOT LIBXML2_FOUND) message(FATAL_ERROR "libXML2 not found") endif () -get_filename_component(LLVM_ROOT "../../llvm/17.0.6" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +get_filename_component(LLVM_ROOT "../../llvm/17.0.7" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0) set(LLVM_SYSTEM_LIBS "-lz -lcurses -lm -lxml2") @@ -36,6 +37,18 @@ if (HAS_UNUSED_BUT_SET_VARIABLE) add_compile_options(-Wno-unused-but-set-variable) endif() +# LLVM 17.0.7 triggers this diagnostic in Clang's own headers on newer Apple toolchains. +# Keep warnings-as-errors for project code, but don't fail the build on this upstream warning. +check_cxx_compiler_flag(-Wno-error=preferred-type-bitfield-enum-conversion HAS_NO_ERROR_PREFERRED_TYPE_BITFIELD_ENUM_CONVERSION) +if (HAS_NO_ERROR_PREFERRED_TYPE_BITFIELD_ENUM_CONVERSION) + add_compile_options(-Wno-error=preferred-type-bitfield-enum-conversion) +endif() + +check_cxx_compiler_flag(-Wno-error=unnecessary-virtual-specifier HAS_NO_ERROR_UNNECESSARY_VIRTUAL_SPECIFIER) +if (HAS_NO_ERROR_UNNECESSARY_VIRTUAL_SPECIFIER) + add_compile_options(-Wno-error=unnecessary-virtual-specifier) +endif() + set(LLVM_LINKER_FLAGS "${LLVM_LINKER_FLAGS} ${LLVM_SYSTEM_LIBS} ${LLVM_LIBS}") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/lib) diff --git a/metadata-generator/src/Binary/binarySerializer.cpp b/metadata-generator/src/Binary/binarySerializer.cpp index 27d99f3b..17880b45 100644 --- a/metadata-generator/src/Binary/binarySerializer.cpp +++ b/metadata-generator/src/Binary/binarySerializer.cpp @@ -254,7 +254,7 @@ static llvm::ErrorOr isStaticFramework(clang::Module* framework) if (path.getError()) { return path.getError(); - } else if (path.get().endswith(".tbd")) { + } else if (path.get().ends_with(".tbd")) { return true; } diff --git a/metadata-generator/src/HeadersParser/Parser.cpp b/metadata-generator/src/HeadersParser/Parser.cpp index 41307597..40ac15ad 100644 --- a/metadata-generator/src/HeadersParser/Parser.cpp +++ b/metadata-generator/src/HeadersParser/Parser.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -31,9 +32,9 @@ static std::error_code addHeaderInclude(StringRef headerName, std::vector>& includes) +static std::error_code addHeaderInclude(FileEntryRef header, std::vector>& includes) { - return addHeaderInclude(header->getName(), includes); + return addHeaderInclude(header.getName(), includes); } static std::error_code collectModuleHeaderIncludes(FileManager& fileMgr, ModuleMap& modMap, const Module* module, std::vector>& includes) @@ -42,17 +43,15 @@ static std::error_code collectModuleHeaderIncludes(FileManager& fileMgr, ModuleM if (!module->isAvailable()) return std::error_code(); - if (module->Umbrella && module->Umbrella.is()) { - const FileEntry* umbrellaHeader = module->Umbrella.get(); - if (std::error_code err = addHeaderInclude(umbrellaHeader, includes)) + if (const auto* umbrellaHeader = std::get_if(&module->Umbrella)) { + if (std::error_code err = addHeaderInclude(*umbrellaHeader, includes)) return err; } - else if (module->Umbrella && module->Umbrella.is()) { - const DirectoryEntryRef umbrellaDir = module->Umbrella.get(); + else if (const auto* umbrellaDir = std::get_if(&module->Umbrella)) { // Add all of the headers we find in this subdirectory. std::error_code ec; SmallString<128> dirNative; - path::native(umbrellaDir.getName(), dirNative); + path::native(umbrellaDir->getName(), dirNative); for (fs::recursive_directory_iterator dir(dirNative.str(), ec), dirEnd; dir != dirEnd && !ec; dir.increment(ec)) { // Check whether this entry has an extension typically associated with headers. if (!llvm::StringSwitch(path::extension(dir->path())) @@ -77,8 +76,8 @@ static std::error_code collectModuleHeaderIncludes(FileManager& fileMgr, ModuleM if (ec) return ec; } else { - for (auto header : module->Headers[Module::HK_Normal]) { - if (auto err = addHeaderInclude(header.Entry, includes)) + for (FileEntryRef header : const_cast(module)->getTopHeaders(fileMgr)) { + if (auto err = addHeaderInclude(header, includes)) return err; } } diff --git a/metadata-generator/src/TypeScript/DefinitionWriter.cpp b/metadata-generator/src/TypeScript/DefinitionWriter.cpp index d081c8fb..6c75345b 100644 --- a/metadata-generator/src/TypeScript/DefinitionWriter.cpp +++ b/metadata-generator/src/TypeScript/DefinitionWriter.cpp @@ -583,7 +583,7 @@ std::string DefinitionWriter::writeMethod(MethodMeta* meta, BaseClassMeta* owner } } - if ((owner->type == MetaType::Protocol && methodDecl.getImplementationControl() == clang::ObjCMethodDecl::ImplementationControl::Optional) || (owner->is(MetaType::Protocol) && meta->getFlags(MethodIsInitializer))) { + if ((owner->type == MetaType::Protocol && methodDecl.getImplementationControl() == clang::ObjCImplementationControl::Optional) || (owner->is(MetaType::Protocol) && meta->getFlags(MethodIsInitializer))) { output << "?"; } diff --git a/package.json b/package.json index a5e0c192..8e301beb 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "build-v8-source-catalyst": "./build_v8_source_catalyst.sh", "build-ios": "node prepare-target ios && ./build_all_ios.sh", "build-vision": "node prepare-target visionos && ./build_all_vision.sh", + "generate-test262-manifest": "node ./scripts/generate-test262-manifest.js", "setup-ci": "./build_metadata_generator.sh", "update-version": "./update_version.sh", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", diff --git a/scripts/generate-test262-manifest.js b/scripts/generate-test262-manifest.js new file mode 100644 index 00000000..e429871c --- /dev/null +++ b/scripts/generate-test262-manifest.js @@ -0,0 +1,271 @@ +const fs = require("fs"); +const path = require("path"); + +const projectRoot = path.resolve(__dirname, ".."); +const config = require(path.join(projectRoot, "TestRunner/app/tests/Test262/config.js")); + +function parseNumber(value, fallback) { + const parsed = Number.parseInt(value, 10); + return Number.isFinite(parsed) ? parsed : fallback; +} + +function toPosix(relativePath) { + return relativePath.split(path.sep).join("/"); +} + +function ensureDirectory(filePath) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); +} + +function writeManifest(payload) { + const outputPath = path.join(projectRoot, config.generatedManifestPath); + ensureDirectory(outputPath); + fs.writeFileSync(outputPath, JSON.stringify(payload, null, 2) + "\n"); +} + +function writeEmptyManifest(reason) { + writeManifest({ + generatedAt: new Date().toISOString(), + enabled: false, + reason, + summary: { + discovered: 0, + included: 0, + excluded: 0 + }, + tests: [] + }); +} + +function readJsonIfPresent(filePath, fallbackValue) { + if (!filePath || !fs.existsSync(filePath)) { + return fallbackValue; + } + + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +function parseList(frontmatter, key) { + const inlineMatch = frontmatter.match(new RegExp("^" + key + ":\\s*\\[(.*?)\\]\\s*$", "m")); + if (inlineMatch) { + return inlineMatch[1] + .split(",") + .map((value) => value.trim()) + .filter(Boolean) + .map((value) => value.replace(/^['\"]|['\"]$/g, "")); + } + + const blockMatch = frontmatter.match(new RegExp("^" + key + ":\\s*\\n((?:\\s+-.*(?:\\n|$))*)", "m")); + if (!blockMatch) { + return []; + } + + return blockMatch[1] + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => line.replace(/^-\s*/, "").trim()); +} + +function parseNegative(frontmatter) { + const match = frontmatter.match(/^negative:\s*\n((?:\s{2,}.*(?:\n|$))*)/m); + if (!match) { + return null; + } + + const block = match[1]; + const phase = (block.match(/^\s+phase:\s*(.+)$/m) || [])[1]; + const type = (block.match(/^\s+type:\s*(.+)$/m) || [])[1]; + + return { + phase: phase ? phase.trim() : "runtime", + type: type ? type.trim() : null + }; +} + +function parseFrontmatter(source) { + const match = source.match(/\/\*---\s*([\s\S]*?)\s*---\*\//); + if (!match) { + return { + flags: [], + features: [], + includes: [], + negative: null + }; + } + + const frontmatter = match[1]; + return { + flags: parseList(frontmatter, "flags"), + features: parseList(frontmatter, "features"), + includes: parseList(frontmatter, "includes"), + negative: parseNegative(frontmatter) + }; +} + +function walkTests(rootDirectory) { + const entries = fs.readdirSync(rootDirectory, { withFileTypes: true }); + const results = []; + + for (const entry of entries) { + const absolutePath = path.join(rootDirectory, entry.name); + if (entry.isDirectory()) { + results.push(...walkTests(absolutePath)); + continue; + } + + if (!entry.isFile()) { + continue; + } + + if (!entry.name.endsWith(".js") || entry.name.includes("_FIXTURE")) { + continue; + } + + results.push(absolutePath); + } + + return results; +} + +function matchesAnyPrefix(relativePath, prefixes) { + if (!prefixes.length) { + return false; + } + + return prefixes.some((prefix) => relativePath.startsWith(prefix)); +} + +function getModes(flags) { + if (flags.includes("onlyStrict")) { + return ["strict"]; + } + + if (flags.includes("noStrict") || !config.expandStrictVariants) { + return ["non-strict"]; + } + + return ["non-strict", "strict"]; +} + +function unique(values) { + return Array.from(new Set(values)); +} + +function normalizePrefixes(prefixes) { + return unique((prefixes || []).map((prefix) => String(prefix).trim()).filter(Boolean)); +} + +function buildManifest() { + const test262Root = path.join(projectRoot, config.projectSubmodulePath); + const testDirectory = path.join(test262Root, "test"); + const manifestTests = []; + const exclusionCounts = {}; + + if (!config.enabled) { + writeEmptyManifest("disabled"); + console.log("Test262 manifest generation skipped because the suite is disabled."); + return; + } + + if (!fs.existsSync(testDirectory)) { + writeEmptyManifest("missing-submodule"); + console.log("Test262 manifest generation skipped because the submodule is missing."); + return; + } + + const curatedPrefixes = normalizePrefixes( + readJsonIfPresent(path.join(projectRoot, config.curatedIncludePathsPath), []) + ); + const requestedPrefixes = (process.env.TEST262_FILTER || "") + .split(",") + .map((value) => value.trim()) + .filter(Boolean); + const activeFilterPrefixes = normalizePrefixes(requestedPrefixes); + const activeIncludePrefixes = normalizePrefixes( + activeFilterPrefixes.length + ? activeFilterPrefixes + : (config.includePaths && config.includePaths.length ? config.includePaths : curatedPrefixes) + ); + const shardCount = parseNumber(process.env.TEST262_SHARD_COUNT, 0); + const shardIndex = parseNumber(process.env.TEST262_SHARD_INDEX, 0); + const limit = parseNumber(process.env.TEST262_LIMIT, config.defaultLimit); + + const discoveredFiles = walkTests(testDirectory) + .map((absolutePath) => ({ + absolutePath, + relativePath: toPosix(path.relative(testDirectory, absolutePath)) + })) + .sort((left, right) => left.relativePath.localeCompare(right.relativePath)); + + for (const testFile of discoveredFiles) { + const source = fs.readFileSync(testFile.absolutePath, "utf8"); + const metadata = parseFrontmatter(source); + const flags = metadata.flags || []; + const features = metadata.features || []; + const includes = unique(config.harnessDefaults.concat((metadata.includes || []).filter((include) => include !== "doneprintHandle.js"))); + let exclusionReason = null; + + if (matchesAnyPrefix(testFile.relativePath, config.excludePaths || [])) { + exclusionReason = "excluded-path"; + } else if (activeFilterPrefixes.length && !matchesAnyPrefix(testFile.relativePath, activeFilterPrefixes)) { + exclusionReason = "filter"; + } else if (activeIncludePrefixes.length && !matchesAnyPrefix(testFile.relativePath, activeIncludePrefixes)) { + exclusionReason = "not-included"; + } else if (flags.some((flag) => (config.unsupportedFlags || []).includes(flag))) { + exclusionReason = "unsupported-flag"; + } else if (includes.some((include) => (config.unsupportedIncludes || []).includes(include))) { + exclusionReason = "unsupported-include"; + } else if (features.some((feature) => (config.unsupportedFeatures || []).includes(feature))) { + exclusionReason = "unsupported-feature"; + } + + if (exclusionReason) { + exclusionCounts[exclusionReason] = (exclusionCounts[exclusionReason] || 0) + 1; + continue; + } + + for (const mode of getModes(flags)) { + manifestTests.push({ + id: testFile.relativePath + " [" + mode + "]", + relativePath: testFile.relativePath, + mode, + async: flags.includes("async"), + includes, + negative: metadata.negative || null + }); + } + } + + let selectedTests = manifestTests; + if (shardCount > 0) { + selectedTests = selectedTests.filter((_, index) => index % shardCount === shardIndex); + } + + if (limit > 0) { + selectedTests = selectedTests.slice(0, limit); + } + + writeManifest({ + generatedAt: new Date().toISOString(), + enabled: true, + reason: "generated", + summary: { + discovered: discoveredFiles.length, + included: selectedTests.length, + excluded: discoveredFiles.length - manifestTests.length, + exclusions: exclusionCounts, + shardCount, + shardIndex, + filter: activeFilterPrefixes, + includePrefixes: activeIncludePrefixes, + curatedIncludePrefixes: curatedPrefixes, + limit + }, + tests: selectedTests + }); + + console.log("Generated Test262 manifest with " + selectedTests.length + " runnable entries from " + discoveredFiles.length + " discovered test files."); +} + +buildManifest(); \ No newline at end of file diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index 2d6a2d4d..8d064025 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -1751,6 +1751,7 @@ isa = PBXNativeTarget; buildConfigurationList = C2DDEB29229EA89200345BFE /* Build configuration list for PBXNativeTarget "TestRunner" */; buildPhases = ( + 3CF8B98F2F8B980100A1A1A1 /* Generate Test262 manifest */, C2F4CBBD22C61D480036B56F /* ShellScript */, C2DDEB12229EA89000345BFE /* Sources */, C2DDEB13229EA89000345BFE /* Frameworks */, @@ -1911,6 +1912,29 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 3CF8B98F2F8B980100A1A1A1 /* Generate Test262 manifest */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/scripts/generate-test262-manifest.js", + "$(SRCROOT)/TestRunner/app/tests/Test262/config.js", + "$(SRCROOT)/TestRunner/app/tests/Test262/curated-prefixes.json", + ); + name = "Generate Test262 manifest"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/TestRunner/app/tests/Test262/generated-manifest.json", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if command -v node >/dev/null 2>&1; then\n cd \"${SRCROOT}\"\n node ./scripts/generate-test262-manifest.js\nelse\n echo \"warning: node is not available, skipping Test262 manifest generation\"\nfi\n"; + }; 2BFE21F02AC1B28000307752 /* Add default metadata */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647;