Improve runtime speed

This commit is contained in:
Simon Sawicki
2025-08-26 20:41:27 +02:00
parent d0490e114a
commit 860b4d8590
7 changed files with 98 additions and 149 deletions

View File

@@ -1,11 +1,10 @@
{
"imports": {
"@babel/generator": "npm:@babel/generator@7.28.3",
"@babel/parser": "npm:@babel/parser@7.28.3",
"@babel/types": "npm:@babel/types@7.28.2",
"@std/assert": "jsr:@std/assert@1",
"@std/io": "jsr:@std/io",
"@std/fs/exists": "jsr:@std/fs/exists"
"@std/fs/exists": "jsr:@std/fs/exists",
"astring": "npm:astring@1.9.0",
"meriyah": "npm:meriyah@6.1.4"
},
"test": {
"exclude": ["./dist"]
@@ -13,6 +12,7 @@
"tasks": {
"download": "deno run --allow-read --allow-write --allow-net=www.youtube.com tests/download.ts",
"test": "deno test --allow-read --allow-env=BABEL_TYPES_8_BREAKING --location 'https://www.youtube.com/watch?v=yt-dlp-wins'",
"ptest": "deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} main$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tcc$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tce$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} es5$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} es6$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tv$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tv_es6$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} phone$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tablet$/\"",
"bundle": "deno bundle --output dist/jsc-deno.js src/main.ts"
}
}

69
deno.lock generated
View File

@@ -3,11 +3,11 @@
"specifiers": {
"jsr:@std/assert@1": "1.0.14",
"jsr:@std/bytes@^1.0.5": "1.0.6",
"jsr:@std/fs@*": "1.0.18",
"jsr:@std/internal@^1.0.10": "1.0.10",
"jsr:@std/io@*": "0.225.2",
"npm:@babel/generator@7.28.3": "7.28.3",
"npm:@babel/parser@7.28.3": "7.28.3",
"npm:@babel/types@7.28.2": "7.28.2"
"npm:astring@1.9.0": "1.9.0",
"npm:meriyah@6.1.4": "6.1.4"
},
"jsr": {
"@std/assert@1.0.14": {
@@ -19,6 +19,9 @@
"@std/bytes@1.0.6": {
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
},
"@std/fs@1.0.18": {
"integrity": "24bcad99eab1af4fde75e05da6e9ed0e0dce5edb71b7e34baacf86ffe3969f3a"
},
"@std/internal@1.0.10": {
"integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7"
},
@@ -30,59 +33,12 @@
}
},
"npm": {
"@babel/generator@7.28.3": {
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
"dependencies": [
"@babel/parser",
"@babel/types",
"@jridgewell/gen-mapping",
"@jridgewell/trace-mapping",
"jsesc"
]
},
"@babel/helper-string-parser@7.27.1": {
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
},
"@babel/helper-validator-identifier@7.27.1": {
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
},
"@babel/parser@7.28.3": {
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"dependencies": [
"@babel/types"
],
"astring@1.9.0": {
"integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
"bin": true
},
"@babel/types@7.28.2": {
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"dependencies": [
"@babel/helper-string-parser",
"@babel/helper-validator-identifier"
]
},
"@jridgewell/gen-mapping@0.3.13": {
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dependencies": [
"@jridgewell/sourcemap-codec",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/resolve-uri@3.1.2": {
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
},
"@jridgewell/sourcemap-codec@1.5.5": {
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"@jridgewell/trace-mapping@0.3.30": {
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
"dependencies": [
"@jridgewell/resolve-uri",
"@jridgewell/sourcemap-codec"
]
},
"jsesc@3.1.0": {
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"bin": true
"meriyah@6.1.4": {
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="
}
},
"workspace": {
@@ -90,9 +46,8 @@
"jsr:@std/assert@1",
"jsr:@std/fs@*",
"jsr:@std/io@*",
"npm:@babel/generator@7.28.3",
"npm:@babel/parser@7.28.3",
"npm:@babel/types@7.28.2"
"npm:astring@1.9.0",
"npm:meriyah@6.1.4"
]
}
}

View File

@@ -1,13 +1,10 @@
import {
type ArrowFunctionExpression,
type BlockStatement,
type Node,
} from "@babel/types";
import { type ESTree } from "meriyah";
import { matchesStructure } from "./utils.ts";
import { type DeepPartial } from "./types.ts";
const identifier: DeepPartial<Node> = {
const identifier: DeepPartial<ESTree.VariableDeclaration> = {
type: "VariableDeclaration",
kind: "var",
declarations: [
{
type: "VariableDeclarator",
@@ -24,35 +21,39 @@ const identifier: DeepPartial<Node> = {
},
},
],
kind: "var",
};
const catchBlockBody = [
{
"type": "ReturnStatement",
"argument": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
left: {
type: "MemberExpression",
object: {
type: "Identifier",
},
"property": {
"type": "NumericLiteral",
computed: true,
property: {
type: "Literal",
},
optional: false,
},
"right": {
"type": "Identifier",
right: {
type: "Identifier",
},
operator: "+",
},
},
] as const;
export function extract(node: Node): ArrowFunctionExpression | null {
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (!matchesStructure(node, identifier)) {
// Fallback search for try { } catch { return X[12] + Y }
let name: string | undefined | null = null;
let block: BlockStatement | null = null;
let block: ESTree.BlockStatement | null | undefined = null;
switch (node.type) {
case "ExpressionStatement": {
if (
@@ -109,17 +110,15 @@ export function extract(node: Node): ArrowFunctionExpression | null {
return makeSolverFuncFromName(firstElement.name);
}
function makeSolverFuncFromName(name: string): ArrowFunctionExpression {
function makeSolverFuncFromName(name: string): ESTree.ArrowFunctionExpression {
return {
type: "ArrowFunctionExpression",
params: [
{
type: "Identifier",
name: "x",
name: "nsig",
},
],
async: false,
expression: true,
body: {
type: "CallExpression",
callee: {
@@ -129,9 +128,13 @@ function makeSolverFuncFromName(name: string): ArrowFunctionExpression {
arguments: [
{
type: "Identifier",
name: "x",
name: "nsig",
},
],
optional: false,
},
async: false,
expression: false,
generator: false,
};
}

View File

@@ -1,11 +1,14 @@
import { type Node } from "@babel/types";
import { parse } from "meriyah";
export const setupNodes: Node[] = [
export const setupNodes = parse(`
globalThis.XMLHttpRequest = { prototype: {} };
const window = Object.assign(Object.create(null), globalThis);
const document = {};
`).body || [
{
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
object: {
@@ -17,23 +20,26 @@ export const setupNodes: Node[] = [
type: "Identifier",
name: "XMLHttpRequest",
},
optional: false,
},
operator: "=",
right: {
type: "ObjectExpression",
properties: [
{
type: "ObjectProperty",
method: false,
type: "Property",
key: {
type: "Identifier",
name: "prototype",
},
computed: false,
shorthand: false,
value: {
type: "ObjectExpression",
properties: [],
},
kind: "init",
computed: false,
method: false,
shorthand: false,
},
],
},
@@ -41,6 +47,7 @@ export const setupNodes: Node[] = [
},
{
type: "VariableDeclaration",
kind: "const",
declarations: [
{
type: "VariableDeclarator",
@@ -61,6 +68,7 @@ export const setupNodes: Node[] = [
type: "Identifier",
name: "assign",
},
optional: false,
},
arguments: [
{
@@ -76,25 +84,29 @@ export const setupNodes: Node[] = [
type: "Identifier",
name: "create",
},
optional: false,
},
arguments: [
{
type: "NullLiteral",
type: "Literal",
value: null,
},
],
optional: false,
},
{
type: "Identifier",
name: "globalThis",
},
],
optional: false,
},
},
],
kind: "const",
},
{
type: "VariableDeclaration",
kind: "const",
declarations: [
{
type: "VariableDeclarator",
@@ -108,6 +120,5 @@ export const setupNodes: Node[] = [
},
},
],
kind: "const",
},
];

View File

@@ -1,30 +1,23 @@
import {
type ArrowFunctionExpression,
type Expression,
type ExpressionStatement,
type FunctionExpression,
type Node,
} from "@babel/types";
import { type ESTree } from "meriyah";
import { matchesStructure } from "./utils.ts";
import { type DeepPartial } from "./types.ts";
const logicalExpression: DeepPartial<ExpressionStatement> = {
const logicalExpression: DeepPartial<ESTree.ExpressionStatement> = {
type: "ExpressionStatement",
expression: {
type: "LogicalExpression",
left: {
type: "Identifier",
},
operator: "&&",
right: {
type: "SequenceExpression",
expressions: [
{
type: "AssignmentExpression",
operator: "=",
left: {
type: "Identifier",
},
operator: "=",
right: {
type: "CallExpression",
callee: {
@@ -33,7 +26,7 @@ const logicalExpression: DeepPartial<ExpressionStatement> = {
arguments: {
or: [
[
{ type: "NumericLiteral" },
{ type: "Literal" },
{
type: "CallExpression",
callee: {
@@ -41,6 +34,7 @@ const logicalExpression: DeepPartial<ExpressionStatement> = {
name: "decodeURIComponent",
},
arguments: [{ type: "Identifier" }],
optional: false,
},
],
[
@@ -51,20 +45,20 @@ const logicalExpression: DeepPartial<ExpressionStatement> = {
name: "decodeURIComponent",
},
arguments: [{ type: "Identifier" }],
optional: false,
},
],
],
},
optional: false,
},
},
{
type: "CallExpression",
},
],
extra: {
parenthesized: true,
},
},
operator: "&&",
},
};
@@ -88,8 +82,12 @@ const identifier = {
}],
} as const;
export function extract(node: Node): ArrowFunctionExpression | null {
if (!matchesStructure(node, identifier as unknown as DeepPartial<Node>)) {
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (
!matchesStructure(node, identifier as unknown as DeepPartial<ESTree.Node>)
) {
return null;
}
const block = (node.type === "ExpressionStatement" &&
@@ -127,8 +125,6 @@ export function extract(node: Node): ArrowFunctionExpression | null {
name: "sig",
},
],
async: false,
expression: true,
body: {
type: "CallExpression",
callee: {
@@ -149,6 +145,10 @@ export function extract(node: Node): ArrowFunctionExpression | null {
name: "sig",
},
],
optional: false,
},
async: false,
expression: false,
generator: false,
};
}

View File

@@ -1,16 +1,12 @@
import { parse } from "@babel/parser";
import { generate } from "@babel/generator";
import { type ArrowFunctionExpression } from "@babel/types";
import { getFunctionNodes } from "./utils.ts";
import { type ESTree, parse } from "meriyah";
import { generate } from "astring";
import { extract as extractSig } from "./sig.ts";
import { extract as extractNsig } from "./nsig.ts";
import { setupNodes } from "./setup.ts";
export function preprocessPlayer(data: string): string {
const ast = parse(data, {
attachComment: false,
});
const body = ast.program.body;
const ast = parse(data);
const body = ast.body;
const block = (() => {
switch (body.length) {
@@ -45,10 +41,10 @@ export function preprocessPlayer(data: string): string {
})();
const found = {
nsig: [] as ArrowFunctionExpression[],
sig: [] as ArrowFunctionExpression[],
nsig: [] as ESTree.ArrowFunctionExpression[],
sig: [] as ESTree.ArrowFunctionExpression[],
};
const plainExpressions = block.body.filter((node) => {
const plainExpressions = block.body.filter((node: ESTree.Node) => {
const nsig = extractNsig(node);
if (nsig) {
found.nsig.push(nsig);
@@ -61,7 +57,7 @@ export function preprocessPlayer(data: string): string {
if (node.expression.type === "AssignmentExpression") {
return true;
}
return node.expression.type === "StringLiteral";
return node.expression.type === "Literal";
}
return true;
});
@@ -74,9 +70,7 @@ export function preprocessPlayer(data: string): string {
const message = `found ${unique.size} ${name} function possibilities`;
throw (
message +
(unique.size
? `: ${options.map((x) => generate(x)["code"]).join(", ")}`
: "")
(unique.size ? `: ${options.map((x) => generate(x)).join(", ")}` : "")
);
}
plainExpressions.push({
@@ -101,13 +95,9 @@ export function preprocessPlayer(data: string): string {
});
}
ast.program.body.splice(0, 0, ...setupNodes);
ast.body.splice(0, 0, ...setupNodes);
const { code } = generate(ast, {
comments: false,
compact: false,
concise: false,
});
const code = generate(ast);
return code;
}

View File

@@ -1,9 +1,8 @@
import { parse } from "@babel/parser";
import { type Node, type Statement } from "@babel/types";
import { type ESTree } from "meriyah";
import { type DeepPartial } from "./types.ts";
export function matchesStructure<T extends Node>(
obj: Node | Node[],
export function matchesStructure<T extends ESTree.Node>(
obj: ESTree.Node | ESTree.Node[],
structure: DeepPartial<T> | readonly DeepPartial<T>[],
): boolean {
if (Array.isArray(structure)) {
@@ -33,15 +32,6 @@ export function matchesStructure<T extends Node>(
return structure === obj;
}
export function getFunctionNodes(f: (...a: unknown[]) => void): Statement[] {
const func = parse(f.toString()).program.body[0];
if (func.type === "FunctionDeclaration") {
return func.body.body;
}
console.error("failed to parse function into nodes");
return [];
}
export function isOneOf<T>(value: unknown, ...of: readonly T[]): value is T {
return of.includes(value as T);
}