hackmud-script-manager
Version:
Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.
721 lines (720 loc) • 26.4 kB
JavaScript
import babelTraverse from "@babel/traverse"
import t from "@babel/types"
import { assert } from "@samual/lib/assert"
import { clearObject } from "@samual/lib/clearObject"
import { validDBMethods } from "../constants.js"
import { getReferencePathsToGlobal } from "./shared.js"
const { default: traverse } = babelTraverse,
globalFunctionsUnder7Characters = [
"Map",
"Set",
"Date",
"JSON",
"Math",
"Array",
"Error",
"isNaN",
"Number",
"Object",
"RegExp",
"String",
"Symbol",
"BigInt"
]
function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scriptName, seclevel = 4 }) {
const warnings = [],
topFunctionName = `_${uniqueId}_SCRIPT_`
let program
traverse(file, {
Program(path) {
program = path
path.skip()
}
})
if (program.scope.hasGlobal("_SOURCE"))
for (const referencePath of getReferencePathsToGlobal("_SOURCE", program))
referencePath.replaceWith(t.stringLiteral(sourceCode))
if (program.scope.hasGlobal("_BUILD_DATE"))
for (const referencePath of getReferencePathsToGlobal("_BUILD_DATE", program))
referencePath.replaceWith(t.numericLiteral(Date.now()))
let uniqueIdScriptUserNeeded = !1
if (program.scope.hasGlobal("_SCRIPT_USER"))
for (const referencePath of getReferencePathsToGlobal("_SCRIPT_USER", program))
if (null == scriptUser) {
uniqueIdScriptUserNeeded = !0
referencePath.replaceWith(t.identifier(`_${uniqueId}_SCRIPT_USER_`))
} else
referencePath.replaceWith(t.stringLiteral(1 == scriptUser ? `$${uniqueId}$SCRIPT_USER$` : scriptUser))
if (program.scope.hasGlobal("_SCRIPT_NAME")) {
warnings.push({
message:
"Global _SCRIPT_NAME is deprecated and will be removed in the next minor release of HSM, use _SCRIPT_SUBNAME instead"
})
for (const referencePath of getReferencePathsToGlobal("_SCRIPT_NAME", program))
referencePath.replaceWith(t.stringLiteral(1 == scriptName ? `$${uniqueId}$SCRIPT_NAME$` : scriptName))
}
if (program.scope.hasGlobal("_SCRIPT_SUBNAME"))
for (const referencePath of getReferencePathsToGlobal("_SCRIPT_SUBNAME", program))
referencePath.replaceWith(t.stringLiteral(1 == scriptName ? `$${uniqueId}$SCRIPT_NAME$` : scriptName))
if (program.scope.hasGlobal("_FULL_SCRIPT_NAME"))
for (const referencePath of getReferencePathsToGlobal("_FULL_SCRIPT_NAME", program))
if (1 == scriptUser || 1 == scriptName)
referencePath.replaceWith(t.stringLiteral(`$${uniqueId}$FULL_SCRIPT_NAME$`))
else if (null == scriptUser) {
uniqueIdScriptUserNeeded = !0
referencePath.replaceWith(
t.binaryExpression(
"+",
t.identifier(`_${uniqueId}_SCRIPT_USER_`),
t.stringLiteral("." + scriptName)
)
)
} else referencePath.replaceWith(t.stringLiteral(`${scriptUser}.${scriptName}`))
let functionDotPrototypeIsReferencedMultipleTimes = !1
if (program.scope.hasGlobal("Function")) {
const FunctionReferencePaths = getReferencePathsToGlobal("Function", program)
if (1 == FunctionReferencePaths.length) {
const referencePath = FunctionReferencePaths[0]
assert(
"MemberExpression" == referencePath.parent.type,
"src/processScript/transform.ts:111:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
assert(
"Identifier" == referencePath.parent.property.type,
"src/processScript/transform.ts:116:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
assert(
"prototype" == referencePath.parent.property.name,
"src/processScript/transform.ts:121:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
referencePath.parentPath.replaceWith(createGetFunctionPrototypeNode())
} else {
for (const referencePath of FunctionReferencePaths) {
assert(
"MemberExpression" == referencePath.parent.type,
"src/processScript/transform.ts:129:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
assert(
"Identifier" == referencePath.parent.property.type,
"src/processScript/transform.ts:134:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
assert(
"prototype" == referencePath.parent.property.name,
"src/processScript/transform.ts:139:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
)
functionDotPrototypeIsReferencedMultipleTimes = !0
referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_FUNCTION_DOT_PROTOTYPE_`))
}
functionDotPrototypeIsReferencedMultipleTimes = !0
}
}
const neededSubscriptLets = new Map()
let detectedSeclevel = 4
program.scope.hasGlobal("$s") &&
warnings.push({
message:
"Subscripts in the form of $s.foo.bar() and #s.foo.bar() are deprecated. Use explicit seclevels instead."
})
for (const fakeSubscriptObjectName of ["$fs", "$4s", "$s"])
program.scope.hasGlobal(fakeSubscriptObjectName) && processFakeSubscriptObject(fakeSubscriptObjectName, 4)
for (const fakeSubscriptObjectName of ["$hs", "$3s"])
if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
detectedSeclevel = 3
processFakeSubscriptObject(fakeSubscriptObjectName, 3)
}
for (const fakeSubscriptObjectName of ["$ms", "$2s"])
if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
detectedSeclevel = 2
processFakeSubscriptObject(fakeSubscriptObjectName, 2)
}
for (const fakeSubscriptObjectName of ["$ls", "$1s"])
if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
detectedSeclevel = 1
processFakeSubscriptObject(fakeSubscriptObjectName, 1)
}
for (const fakeSubscriptObjectName of ["$ns", "$0s"])
if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
detectedSeclevel = 0
processFakeSubscriptObject(fakeSubscriptObjectName, 0)
}
seclevel = Math.min(seclevel, detectedSeclevel)
const neededDbMethodLets = new Set()
if (program.scope.hasGlobal("$db"))
for (const referencePath of getReferencePathsToGlobal("$db", program)) {
assert("MemberExpression" == referencePath.parentPath.node.type, "src/processScript/transform.ts:199:69")
assert("Identifier" == referencePath.parentPath.node.property.type, "src/processScript/transform.ts:200:72")
const databaseOpMethodName = referencePath.parentPath.node.property.name
assert(
validDBMethods.includes(databaseOpMethodName),
`src/processScript/transform.ts:206:8 invalid db method "${databaseOpMethodName}", valid db methods are "${validDBMethods.join('", "')}"`
)
if ("CallExpression" == referencePath.parentPath.parentPath?.type)
referencePath.parentPath.replaceWith(t.identifier(`$${uniqueId}$DB$${databaseOpMethodName}$`))
else {
referencePath.parentPath.replaceWith(
t.identifier(`_${uniqueId}_CONSOLE_METHOD_${databaseOpMethodName}_`)
)
neededDbMethodLets.add(databaseOpMethodName)
}
}
let needDebugLet = !1
if (program.scope.hasGlobal("$D"))
for (const referencePath of getReferencePathsToGlobal("$D", program))
if ("CallExpression" == referencePath.parentPath.type)
referencePath.replaceWith(t.identifier(`$${uniqueId}$DEBUG$`))
else {
referencePath.replaceWith(t.identifier(`_${uniqueId}_DEBUG_`))
needDebugLet = !0
}
if (program.scope.hasGlobal("$FMCL"))
for (const referencePath of getReferencePathsToGlobal("$FMCL", program))
referencePath.replaceWith(t.identifier(`$${uniqueId}$FMCL$`))
let needG = program.scope.hasGlobal("$G")
if (needG)
for (const referencePath of getReferencePathsToGlobal("$G", program))
referencePath.replaceWith(t.identifier(`_${uniqueId}_G_`))
if (program.scope.hasGlobal("_SECLEVEL"))
for (const referencePath of getReferencePathsToGlobal("_SECLEVEL", program))
referencePath.replaceWith(t.numericLiteral(seclevel))
let needGetPrototypeOf = !1,
needHasOwn = !1
if (program.scope.hasGlobal("Object"))
for (const referencePath of getReferencePathsToGlobal("Object", program))
if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:256:64")
if ("getPrototypeOf" == referencePath.parent.property.name) {
referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_GET_PROTOTYPE_OF_`))
needGetPrototypeOf = !0
} else if ("hasOwn" == referencePath.parent.property.name) {
referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_HAS_OWN_`))
needHasOwn = !0
}
}
const consoleMethodsReferenced = new Set()
if (program.scope.hasGlobal("console"))
for (const referencePath of getReferencePathsToGlobal("console", program))
if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:274:64")
referencePath.parentPath.replaceWith(
t.identifier(`_${uniqueId}_CONSOLE_METHOD_${referencePath.parent.property.name}_`)
)
consoleMethodsReferenced.add(referencePath.parent.property.name)
}
const lastStatement = program.node.body.at(-1)
let exportDefaultName
assert(lastStatement, "src/processScript/transform.ts:288:27 program is empty")
if ("ExportNamedDeclaration" == lastStatement.type) {
program.node.body.pop()
for (const specifier of lastStatement.specifiers) {
assert(
"ExportSpecifier" == specifier.type,
`src/processScript/transform.ts:294:51 ${specifier.type} is currently unsupported`
)
if (
"default" !=
("Identifier" == specifier.exported.type ? specifier.exported.name : specifier.exported.value)
)
throw Error("Only default exports are supported")
exportDefaultName = specifier.local.name
}
}
const globalBlock = t.blockStatement([])
let mainFunction
for (const statement of program.node.body)
if ("VariableDeclaration" == statement.type)
for (const declarator of statement.declarations)
if (
"Identifier" != declarator.id.type ||
declarator.id.name != exportDefaultName ||
!declarator.init ||
("FunctionExpression" != declarator.init.type &&
"ArrowFunctionExpression" != declarator.init.type) ||
declarator.init.async ||
declarator.init.generator
) {
for (const identifierName in t.getBindingIdentifiers(declarator.id)) {
identifierName == exportDefaultName &&
(mainFunction = t.functionDeclaration(
t.identifier(topFunctionName),
[t.identifier("context"), t.identifier("args")],
t.blockStatement([
t.returnStatement(t.callExpression(t.identifier(exportDefaultName), []))
])
))
globalBlock.body.push(
t.variableDeclaration("let", [t.variableDeclarator(t.identifier(identifierName))])
)
}
declarator.init &&
globalBlock.body.push(
t.expressionStatement(t.assignmentExpression("=", declarator.id, declarator.init))
)
} else
mainFunction = t.functionDeclaration(
t.identifier(topFunctionName),
declarator.init.params,
"BlockStatement" == declarator.init.body.type
? declarator.init.body
: t.blockStatement([t.returnStatement(declarator.init.body)])
)
else
"FunctionDeclaration" == statement.type
? statement.id.name == exportDefaultName
? (mainFunction = statement)
: globalBlock.body.push(
t.variableDeclaration("let", [
t.variableDeclarator(
statement.id,
t.functionExpression(
void 0,
statement.params,
statement.body,
statement.generator,
statement.async
)
)
])
)
: globalBlock.body.push(statement)
mainFunction ||= t.functionDeclaration(
t.identifier(topFunctionName),
[t.identifier("context"), t.identifier("args")],
t.blockStatement([])
)
if (uniqueIdScriptUserNeeded) {
const mainFunctionParams = mainFunction.params
mainFunction.params = [t.restElement(t.identifier(`_${uniqueId}_PARAMS_`))]
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(t.arrayPattern(mainFunctionParams), t.identifier(`_${uniqueId}_PARAMS_`)),
t.variableDeclarator(
t.arrayPattern([t.identifier(`_${uniqueId}_SCRIPT_USER_`)]),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.memberExpression(t.identifier(`_${uniqueId}_PARAMS_`), t.numericLiteral(0), !0),
t.identifier("this_script")
),
t.identifier("split")
),
[t.stringLiteral(".")]
)
)
])
)
}
program.node.body = [mainFunction]
if (globalBlock.body.length) {
program.scope.crawl()
const globalBlockVariables = new Set()
let hoistedGlobalBlockFunctions = 0
for (const [globalBlockIndex, globalBlockStatement] of [...globalBlock.body.entries()].reverse())
if ("VariableDeclaration" == globalBlockStatement.type) {
assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:408:59")
const declarator = globalBlockStatement.declarations[0]
assert(
"Identifier" == declarator.id.type,
`src/processScript/transform.ts:412:51 declarator.id.type was "${declarator.id.type}"`
)
program.scope.crawl()
if (program.scope.hasGlobal(declarator.id.name)) {
globalBlock.body.splice(globalBlockIndex, 1)
const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
[globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
program.scope.crawl()
if (
!declarator.init ||
("FunctionExpression" != declarator.init.type &&
"ArrowFunctionExpression" != declarator.init.type) ||
Object.keys(program.scope.globals).some(global => globalBlockVariables.has(global))
) {
const binding = program.scope.getBinding(declarator.id.name)
assert(binding, "src/processScript/transform.ts:431:23")
for (const referencePath of binding.referencePaths) {
assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:434:56")
referencePath.replaceWith(
t.memberExpression(
t.identifier(`_${uniqueId}_G_`),
t.identifier(referencePath.node.name)
)
)
needG = !0
}
for (const referencePath of binding.constantViolations)
if ("AssignmentExpression" == referencePath.node.type)
for (const [name, node] of Object.entries(t.getBindingIdentifiers(referencePath.node)))
if (name == declarator.id.name) {
clearObject(node)
Object.assign(
node,
t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(name))
)
needG = !0
}
globalBlockPath.remove()
globalBlockStatementPath.remove()
if (declarator.init) {
globalBlock.body.splice(
globalBlockIndex,
0,
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier(`_${uniqueId}_G_`),
t.identifier(declarator.id.name)
),
declarator.init
)
)
)
needG = !0
}
} else {
globalBlockPath.remove()
globalBlockStatementPath.remove()
mainFunction.body.body.unshift(globalBlockStatement)
hoistedGlobalBlockFunctions++
}
} else globalBlockVariables.add(declarator.id.name)
} else if ("ClassDeclaration" == globalBlockStatement.type) {
program.scope.crawl()
assert(globalBlockStatement.id, "src/processScript/transform.ts:491:37")
if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
globalBlock.body.splice(globalBlockIndex, 1)
const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
[globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
program.scope.crawl()
const binding = program.scope.getBinding(globalBlockStatement.id.name)
assert(binding, "src/processScript/transform.ts:503:22")
for (const referencePath of binding.referencePaths) {
assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:506:55")
referencePath.replaceWith(
t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(referencePath.node.name))
)
needG = !0
}
globalBlockPath.remove()
globalBlockStatementPath.remove()
globalBlock.body.splice(
globalBlockIndex,
0,
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier(`_${uniqueId}_G_`),
t.identifier(globalBlockStatement.id.name)
),
t.classExpression(
void 0,
globalBlockStatement.superClass,
globalBlockStatement.body,
globalBlockStatement.decorators
)
)
)
)
needG = !0
}
}
globalBlock.body.length &&
mainFunction.body.body.splice(
hoistedGlobalBlockFunctions,
0,
t.ifStatement(t.unaryExpression("!", t.identifier(`$${uniqueId}$FMCL$`)), globalBlock)
)
}
functionDotPrototypeIsReferencedMultipleTimes &&
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(`_${uniqueId}_FUNCTION_DOT_PROTOTYPE_`),
createGetFunctionPrototypeNode()
)
])
)
needGetPrototypeOf &&
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.objectPattern([
t.objectProperty(t.identifier("get"), t.identifier(`_${uniqueId}_DUNDER_PROTO_GETTER_`))
]),
t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("getOwnPropertyDescriptor")),
[
t.memberExpression(t.identifier("Object"), t.identifier("prototype")),
t.stringLiteral("__proto__")
]
)
),
t.variableDeclarator(
t.identifier(`_${uniqueId}_GET_PROTOTYPE_OF_`),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier(
globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
),
t.identifier("call")
),
t.identifier("bind")
),
[t.identifier(`_${uniqueId}_DUNDER_PROTO_GETTER_`)]
)
)
])
)
needHasOwn &&
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(`_${uniqueId}_HAS_OWN_`),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier(
globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
),
t.identifier("call")
),
t.identifier("bind")
),
[
t.memberExpression(
t.memberExpression(t.identifier("Object"), t.identifier("prototype")),
t.identifier("hasOwnProperty")
)
]
)
)
])
)
consoleMethodsReferenced.size &&
mainFunction.body.body.unshift(
t.variableDeclaration(
"let",
[...consoleMethodsReferenced].map(name =>
t.variableDeclarator(
t.identifier(`_${uniqueId}_CONSOLE_METHOD_${name}_`),
t.arrowFunctionExpression(
[t.restElement(t.identifier("args"))],
t.unaryExpression(
"void",
t.callExpression(t.identifier(`$${uniqueId}$DEBUG$`), [t.identifier("args")])
)
)
)
)
)
)
neededDbMethodLets.size &&
mainFunction.body.body.unshift(
t.variableDeclaration(
"let",
[...neededDbMethodLets].map(name => {
const getArgs = () =>
"ObjectId" == name
? []
: "i" == name || "r" == name
? [t.identifier("a")]
: [t.identifier("a"), t.identifier("b")]
return t.variableDeclarator(
t.identifier(`_${uniqueId}_CONSOLE_METHOD_${name}_`),
t.arrowFunctionExpression(
getArgs(),
t.callExpression(t.identifier(`$${uniqueId}$DB$${name}$`), getArgs())
)
)
})
)
)
needDebugLet &&
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(`_${uniqueId}_DEBUG_`),
t.callExpression(t.identifier(`$${uniqueId}$DEBUG$`), [t.identifier("a")])
)
])
)
neededSubscriptLets.size &&
mainFunction.body.body.unshift(
t.variableDeclaration(
"let",
[...neededSubscriptLets].map(([name, seclevel]) =>
t.variableDeclarator(
t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`),
t.arrowFunctionExpression(
[t.restElement(t.identifier("args"))],
t.callExpression(t.identifier(`$${uniqueId}$${seclevel}$SUBSCRIPT$${name}$`), [
t.spreadElement(t.identifier("args"))
])
)
)
)
)
)
needG &&
mainFunction.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(`_${uniqueId}_G_`), t.identifier(`$${uniqueId}$GLOBAL$`))
])
)
traverse(file, {
BlockStatement({ node: blockStatement }) {
for (const [index, functionDeclaration] of blockStatement.body.entries())
if ("FunctionDeclaration" == functionDeclaration.type && !functionDeclaration.generator) {
blockStatement.body.splice(index, 1)
blockStatement.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
functionDeclaration.id,
t.arrowFunctionExpression(
functionDeclaration.params,
functionDeclaration.body,
functionDeclaration.async
)
)
])
)
}
},
ClassBody({ node: classBody, scope, parent }) {
assert(t.isClass(parent), "src/processScript/transform.ts:701:30")
let thisIsReferenced = !1
for (const classMethod of classBody.body) {
if ("ClassMethod" != classMethod.type) continue
let methodReferencesThis = !1
traverse(
classMethod.body,
{
ThisExpression(path) {
methodReferencesThis = !0
thisIsReferenced = !0
path.replaceWith(t.identifier(`_${uniqueId}_THIS_`))
},
Function(path) {
"ArrowFunctionExpression" != path.node.type && path.skip()
}
},
scope
)
if (methodReferencesThis)
if ("constructor" != classMethod.kind)
classMethod.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(`_${uniqueId}_THIS_`),
t.callExpression(t.memberExpression(t.super(), t.identifier("valueOf")), [])
)
])
)
else {
const superCalls = []
traverse(
classMethod.body,
{
CallExpression(path) {
"Super" == path.node.callee.type && superCalls.push(path)
}
},
scope
)
if (superCalls.length)
if (
1 == superCalls.length &&
"ExpressionStatement" == superCalls[0].parent.type &&
superCalls[0].parentPath.parentPath.parent == classMethod
)
superCalls[0].parentPath.replaceWith(
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(`_${uniqueId}_THIS_`), superCalls[0].node)
])
)
else {
for (const path of superCalls)
path.replaceWith(
t.assignmentExpression("=", t.identifier(`_${uniqueId}_THIS_`), path.node)
)
classMethod.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(`_${uniqueId}_THIS_`))
])
)
}
else
classMethod.body.body.unshift(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(`_${uniqueId}_THIS_`),
t.callExpression(t.super(), [])
)
])
)
}
}
!parent.superClass && thisIsReferenced && (parent.superClass = t.identifier("Object"))
},
VariableDeclaration({ node: variableDeclaration }) {
"const" == variableDeclaration.kind && (variableDeclaration.kind = "let")
},
ThisExpression: path => {
path.replaceWith(t.identifier("undefined"))
},
BigIntLiteral(path) {
const bigIntAsNumber = Number(path.node.value)
path.replaceWith(
t.callExpression(t.identifier("BigInt"), [
BigInt(bigIntAsNumber) == BigInt(path.node.value)
? t.numericLiteral(bigIntAsNumber)
: t.stringLiteral(path.node.value)
])
)
}
})
return { file, seclevel, warnings }
function createGetFunctionPrototypeNode() {
const name = globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
return t.memberExpression(
name ? t.identifier(name) : t.arrowFunctionExpression([t.identifier("_")], t.identifier("_")),
t.identifier("__proto__")
)
}
function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:811:60")
assert("Identifier" == referencePath.parent.property.type)
assert(
"MemberExpression" == referencePath.parentPath.parentPath?.node.type,
"src/processScript/transform.ts:813:81"
)
assert(
"Identifier" == referencePath.parentPath.parentPath.node.property.type,
"src/processScript/transform.ts:814:83"
)
assert(
/^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
`src/processScript/transform.ts:818:8 invalid user "${referencePath.parent.property.name}" in subscript`
)
assert(
/^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
`src/processScript/transform.ts:823:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
)
if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
referencePath.parentPath.parentPath.replaceWith(
t.identifier(
`$${uniqueId}$${seclevel}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
)
)
else {
const name = `${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}`
referencePath.parentPath.parentPath.replaceWith(t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`))
const maxSecLevel = Math.max(neededSubscriptLets.get(name) || 0, seclevel)
neededSubscriptLets.set(name, maxSecLevel)
}
}
}
}
export { transform }