@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 7.08 kB
JavaScript
import*as Acorn from"../../third_party/acorn/acorn.js";import{ECMA_VERSION}from"./AcornTokenizer.js";export function parseScopes(e,s="script"){let t=null;try{t=Acorn.parse(e,{ecmaVersion:ECMA_VERSION,allowAwaitOutsideFunction:!0,ranges:!1,sourceType:s})}catch{return null}return new ScopeVariableAnalysis(t).run()}export class Scope{variables=new Map;parent;start;end;children=[];constructor(e,s,t){this.start=e,this.end=s,this.parent=t,t&&t.children.push(this)}export(){const e=[];for(const s of this.variables){const t=[];for(const e of s[1].uses)t.push(e.offset);e.push({name:s[0],kind:s[1].definitionKind,offsets:t})}const s=this.children.map((e=>e.export()));return{start:this.start,end:this.end,variables:e,children:s}}addVariable(e,s,t,o){const r=this.variables.get(e),i={offset:s,scope:this,isShorthandAssignmentProperty:o};r?(0===r.definitionKind&&(r.definitionKind=t),r.uses.push(i)):this.variables.set(e,{definitionKind:t,uses:[i]})}findBinders(e){const s=[];let t=this;for(;null!==t;){const o=t.variables.get(e);o&&0!==o.definitionKind&&s.push(o),t=t.parent}return s}#e(e,s){const t=this.variables.get(e);t?(t.uses.push(...s.uses),2===s.definitionKind?(console.assert(1!==t.definitionKind),0===t.definitionKind&&(t.definitionKind=s.definitionKind)):console.assert(0===s.definitionKind)):this.variables.set(e,s)}finalizeToParent(e){if(!this.parent)throw console.error("Internal error: wrong nesting in scope analysis."),new Error("Internal error");const s=[];for(const[t,o]of this.variables.entries())(0===o.definitionKind||2===o.definitionKind&&!e)&&(this.parent.#e(t,o),s.push(t));s.forEach((e=>this.variables.delete(e)))}}export class ScopeVariableAnalysis{#s;#t=new Set;#o;#r;constructor(e){this.#r=e,this.#s=new Scope(e.start,e.end,null),this.#o=this.#s}run(){return this.#i(this.#r),this.#s}#i(e){if(null!==e)switch(e.type){case"AwaitExpression":case"SpreadElement":case"ThrowStatement":case"UnaryExpression":case"UpdateExpression":this.#i(e.argument);break;case"ArrayExpression":case"ArrayPattern":e.elements.forEach((e=>this.#i(e)));break;case"ExpressionStatement":case"ChainExpression":this.#i(e.expression);break;case"Program":console.assert(this.#o===this.#s),e.body.forEach((e=>this.#i(e))),console.assert(this.#o===this.#s);break;case"ArrowFunctionExpression":this.#a(e.start,e.end),e.params.forEach(this.#n.bind(this,2,!1)),"BlockStatement"===e.body.type?e.body.body.forEach(this.#i.bind(this)):this.#i(e.body),this.#c(!0);break;case"AssignmentExpression":case"AssignmentPattern":case"BinaryExpression":case"LogicalExpression":this.#i(e.left),this.#i(e.right);break;case"BlockStatement":this.#a(e.start,e.end),e.body.forEach(this.#i.bind(this)),this.#c(!1);break;case"CallExpression":case"NewExpression":this.#i(e.callee),e.arguments.forEach(this.#i.bind(this));break;case"VariableDeclaration":{const s="var"===e.kind?2:1;e.declarations.forEach(this.#p.bind(this,s));break}case"CatchClause":this.#a(e.start,e.end),this.#n(1,!1,e.param),this.#i(e.body),this.#c(!1);break;case"ClassBody":e.body.forEach(this.#i.bind(this));break;case"ClassDeclaration":this.#n(1,!1,e.id),this.#i(e.superClass??null),this.#i(e.body);break;case"ClassExpression":this.#i(e.superClass??null),this.#i(e.body);break;case"ConditionalExpression":this.#i(e.test),this.#i(e.consequent),this.#i(e.alternate);break;case"DoWhileStatement":this.#i(e.body),this.#i(e.test);break;case"ForInStatement":case"ForOfStatement":this.#a(e.start,e.end),this.#i(e.left),this.#i(e.right),this.#i(e.body),this.#c(!1);break;case"ForStatement":this.#a(e.start,e.end),this.#i(e.init??null),this.#i(e.test??null),this.#i(e.update??null),this.#i(e.body),this.#c(!1);break;case"FunctionDeclaration":this.#n(2,!1,e.id),this.#a(e.id?.end??e.start,e.end),this.#d("this",e.start,3),this.#d("arguments",e.start,3),e.params.forEach(this.#n.bind(this,1,!1)),e.body.body.forEach(this.#i.bind(this)),this.#c(!0);break;case"FunctionExpression":this.#a(e.id?.end??e.start,e.end),this.#d("this",e.start,3),this.#d("arguments",e.start,3),e.params.forEach(this.#n.bind(this,1,!1)),e.body.body.forEach(this.#i.bind(this)),this.#c(!0);break;case"Identifier":this.#d(e.name,e.start);break;case"IfStatement":this.#i(e.test),this.#i(e.consequent),this.#i(e.alternate??null);break;case"LabeledStatement":this.#i(e.body);break;case"MetaProperty":case"PrivateIdentifier":case"BreakStatement":case"ContinueStatement":case"DebuggerStatement":case"EmptyStatement":case"Literal":case"Super":case"TemplateElement":case"ImportDeclaration":case"ImportDefaultSpecifier":case"ImportNamespaceSpecifier":case"ImportSpecifier":case"ImportExpression":case"ExportAllDeclaration":case"ExportDefaultDeclaration":case"ExportNamedDeclaration":case"ExportSpecifier":break;case"MethodDefinition":e.computed&&this.#i(e.key),this.#i(e.value);break;case"MemberExpression":this.#i(e.object),e.computed&&this.#i(e.property);break;case"ObjectExpression":case"ObjectPattern":e.properties.forEach(this.#i.bind(this));break;case"PropertyDefinition":e.computed&&this.#i(e.key),this.#i(e.value??null);break;case"Property":e.shorthand?(console.assert("Identifier"===e.value.type),console.assert("Identifier"===e.key.type),console.assert(e.value.name===e.key.name),this.#d(e.value.name,e.value.start,0,!0)):(e.computed&&this.#i(e.key),this.#i(e.value));break;case"RestElement":this.#n(1,!1,e.argument);break;case"ReturnStatement":case"YieldExpression":this.#i(e.argument??null);break;case"SequenceExpression":case"TemplateLiteral":e.expressions.forEach(this.#i.bind(this));break;case"SwitchCase":this.#i(e.test??null),e.consequent.forEach(this.#i.bind(this));break;case"SwitchStatement":this.#i(e.discriminant),e.cases.forEach(this.#i.bind(this));break;case"TaggedTemplateExpression":this.#i(e.tag),this.#i(e.quasi);break;case"ThisExpression":this.#d("this",e.start);break;case"TryStatement":this.#i(e.block),this.#i(e.handler??null),this.#i(e.finalizer??null);break;case"WithStatement":this.#i(e.object),this.#i(e.body);break;case"WhileStatement":this.#i(e.test),this.#i(e.body);break;case"VariableDeclarator":console.error("Should not encounter VariableDeclarator in general traversal.")}}getFreeVariables(){const e=new Map;for(const[s,t]of this.#s.variables)0===t.definitionKind&&e.set(s,t.uses);return e}getAllNames(){return this.#t}#a(e,s){this.#o=new Scope(e,s,this.#o)}#c(e){if(null===this.#o.parent)throw console.error("Internal error: wrong nesting in scope analysis."),new Error("Internal error");this.#o.finalizeToParent(e),this.#o=this.#o.parent}#d(e,s,t=0,o=!1){this.#t.add(e),this.#o.addVariable(e,s,t,o)}#n(e,s,t){if(null!==t)switch(t.type){case"ArrayPattern":t.elements.forEach(this.#n.bind(this,e,!1));break;case"AssignmentPattern":this.#n(e,s,t.left),this.#i(t.right);break;case"Identifier":this.#d(t.name,t.start,e,s);break;case"MemberExpression":this.#i(t.object),t.computed&&this.#i(t.property);break;case"ObjectPattern":t.properties.forEach(this.#n.bind(this,e,!1));break;case"Property":t.computed&&this.#i(t.key),this.#n(e,t.shorthand,t.value);break;case"RestElement":this.#n(e,!1,t.argument)}}#p(e,s){this.#n(e,!1,s.id),this.#i(s.init??null)}}