objj-transpiler
Version:
JavaScript (ECMAScript) and Objective-J compiler with preprocessor
1 lines • 281 kB
Source Map (JSON)
{"version":3,"file":"objj-transpiler.cjs","sources":["../src/warning.js","../src/scope.js","../src/buffer.js","../src/options.js","../src/definition.js","../src/class-def.js","../src/protocol.js","../src/util.js","../src/walk.js","../src/compiler.js","../src/index.js"],"sourcesContent":["import * as objjParser from 'objj-parser'\n\nexport class GlobalVariableMaybeWarning {\n constructor (/* String */ aMessage, /* SpiderMonkey AST node */ node, /* String */ code) {\n this.message = createMessage(aMessage, node, code)\n this.node = node\n }\n\n checkIfWarning = function (/* Scope */ st) {\n const identifier = this.node.name\n return !st.getLvar(identifier) && typeof global[identifier] === 'undefined' && (typeof window === 'undefined' || typeof window[identifier] === 'undefined') && !st.compiler.getClassDef(identifier)\n }\n\n isEqualTo = function (/* GlobalVariableMaybeWarning */ aWarning) {\n if (this.message.message !== aWarning.message.message) return false\n if (this.node.start !== aWarning.node.start) return false\n if (this.node.end !== aWarning.node.end) return false\n\n return true\n }\n}\n\nexport const warningUnusedButSetVariable = { name: 'unused-but-set-variable' }\nexport const warningShadowIvar = { name: 'shadow-ivar' }\nexport const warningCreateGlobalInsideFunctionOrMethod = { name: 'create-global-inside-function-or-method' }\nexport const warningUnknownClassOrGlobal = { name: 'unknown-class-or-global' }\nexport const warningUnknownIvarType = { name: 'unknown-ivar-type' }\nexport const AllWarnings = [warningUnusedButSetVariable, warningShadowIvar, warningCreateGlobalInsideFunctionOrMethod, warningUnknownClassOrGlobal, warningUnknownIvarType]\n\nexport function getLineOffsets (code, offset) {\n let lineEnd = offset\n while (lineEnd < code.length) {\n if (objjParser.isNewLine(code.charCodeAt(lineEnd))) {\n break\n }\n lineEnd++\n }\n let lineStart = offset\n while (lineStart > 0) {\n if (objjParser.isNewLine(code.charCodeAt(lineStart))) {\n lineStart++\n break\n }\n lineStart--\n }\n return { lineStart, lineEnd }\n}\n\nexport function createMessage (/* String */ aMessage, /* SpiderMonkey AST node */ node, /* String */ code) {\n const message = {}\n const { lineStart, lineEnd } = getLineOffsets(code, node.start)\n const loc = objjParser.getLineInfo(code, node.start)\n message.loc = loc\n message.lineStart = lineStart\n message.lineEnd = lineEnd\n //message.line = line\n //message.column = column\n message.message = aMessage\n // As a SyntaxError object can't change the property 'line' we also set the property 'messageOnLine'\n message.messageOnLine = loc.line\n message.messageOnColumn = loc.column\n message.messageForNode = node\n message.messageType = 'WARNING'\n message.messageForLine = code.substring(message.lineStart, message.lineEnd)\n\n return message\n}\n","import { warningUnusedButSetVariable, createMessage } from './warning.js'\n\nexport class Scope {\n constructor (prev, base) {\n this.vars = Object.create(null)\n\n if (base) for (const key in base) this[key] = base[key]\n this.prev = prev\n\n if (prev) {\n this.compiler = prev.compiler\n this.nodeStack = prev.nodeStack.slice(0)\n this.nodePriorStack = prev.nodePriorStack.slice(0)\n this.nodeStackOverrideType = prev.nodeStackOverrideType.slice(0)\n } else {\n this.nodeStack = []\n this.nodePriorStack = []\n this.nodeStackOverrideType = []\n }\n }\n\n toString () {\n return this.ivars ? 'ivars: ' + JSON.stringify(this.ivars) : '<No ivars>'\n }\n\n compiler () {\n return this.compiler\n }\n\n rootScope () {\n return this.prev ? this.prev.rootScope() : this\n }\n\n isRootScope () {\n return !this.prev\n }\n\n currentClassName () {\n return this.classDef ? this.classDef.name : this.prev ? this.prev.currentClassName() : null\n }\n\n currentProtocolName () {\n return this.protocolDef ? this.protocolDef.name : this.prev ? this.prev.currentProtocolName() : null\n }\n\n getIvarForCurrentClass (/* String */ ivarName) {\n if (this.ivars) {\n const ivar = this.ivars[ivarName]\n if (ivar) { return ivar }\n }\n\n const prev = this.prev\n\n // Stop at the class declaration\n if (prev && !this.classDef) { return prev.getIvarForCurrentClass(ivarName) }\n\n return null\n }\n\n getLvarScope (/* String */ lvarName, /* BOOL */ stopAtMethod) {\n if (this.vars) {\n const lvar = this.vars[lvarName]\n if (lvar) { return this }\n }\n\n const prev = this.prev\n\n // Stop at the method declaration\n if (prev && (!stopAtMethod || !this.methodType)) { return prev.getLvarScope(lvarName, stopAtMethod) }\n\n return this\n }\n\n getLvar (/* String */ lvarName, /* BOOL */ stopAtMethod) {\n if (this.vars) {\n const lvar = this.vars[lvarName]\n if (lvar) { return lvar }\n }\n\n const prev = this.prev\n\n // Stop at the method declaration\n if (prev && (!stopAtMethod || !this.methodType)) { return prev.getLvar(lvarName, stopAtMethod) }\n\n return null\n }\n\n getVarScope () {\n const prev = this.prev\n\n return prev ? prev.getVarScope() : this\n }\n\n currentMethodType () {\n return this.methodType ? this.methodType : this.prev ? this.prev.currentMethodType() : null\n }\n\n copyAddedSelfToIvarsToParent () {\n if (this.prev && this.addedSelfToIvars) {\n for (const key in this.addedSelfToIvars) {\n const addedSelfToIvar = this.addedSelfToIvars[key]\n const scopeAddedSelfToIvar = (this.prev.addedSelfToIvars || (this.prev.addedSelfToIvars = Object.create(null)))[key] || (this.prev.addedSelfToIvars[key] = [])\n\n scopeAddedSelfToIvar.push.apply(scopeAddedSelfToIvar, addedSelfToIvar) // Append at end in parent scope\n }\n }\n }\n\n addMaybeWarning (warning) {\n const rootScope = this.rootScope()\n let maybeWarnings = rootScope._maybeWarnings\n\n if (!maybeWarnings) { rootScope._maybeWarnings = maybeWarnings = [warning] } else {\n const lastWarning = maybeWarnings[maybeWarnings.length - 1]\n\n // MessageSendExpression (and maybe others) will walk some expressions multible times and\n // possible generate warnings multible times. Here we check if this warning is already added\n if (!lastWarning.isEqualTo(warning)) { maybeWarnings.push(warning) }\n }\n }\n\n variablesNotReadWarnings () {\n const compiler = this.compiler\n\n // The warning option must be turned on. We can't be top scope. The scope must have some variables\n if (compiler.options.warnings.includes(warningUnusedButSetVariable) && this.prev && this.vars) {\n for (const key in this.vars) {\n const lvar = this.vars[key]\n\n if (!lvar.isRead && (lvar.type === 'var' || lvar.type === 'let' || lvar.type === 'const')) {\n // print(\"Variable '\" + key + \"' is never read: \" + lvar.type + \", line: \" + lvar.node.start);\n compiler.addWarning(createMessage(\"Variable '\" + key + \"' is never read\", lvar.node, compiler.source))\n }\n }\n }\n }\n\n maybeWarnings () {\n return this.rootScope()._maybeWarnings\n }\n\n pushNode (node, overrideType) {\n // Here we push 3 things to a stack. The node, override type and an array that can keep track of prior nodes on this level.\n // The current node is also pushed to the last prior array.\n // Special case when node is the same as the parent node. This happends when using an override type when walking the AST\n // The same prior list is then used instead of a new empty one.\n const nodePriorStack = this.nodePriorStack\n const length = nodePriorStack.length\n const lastPriorList = length ? nodePriorStack[length - 1] : null\n const lastNode = length ? this.nodeStack[length - 1] : null\n // First add this node to parent list of nodes, if it has one\n if (lastPriorList) {\n if (lastNode !== node) {\n // If not the same node push the node\n lastPriorList.push(node)\n }\n }\n // Use the last prior list if it is the same node\n nodePriorStack.push(lastNode === node ? lastPriorList : [])\n this.nodeStack.push(node)\n this.nodeStackOverrideType.push(overrideType)\n }\n\n popNode () {\n this.nodeStackOverrideType.pop()\n this.nodePriorStack.pop()\n return this.nodeStack.pop()\n }\n\n currentNode () {\n const nodeStack = this.nodeStack\n return nodeStack[nodeStack.length - 1]\n }\n\n currentOverrideType () {\n const nodeStackOverrideType = this.nodeStackOverrideType\n return nodeStackOverrideType[nodeStackOverrideType.length - 1]\n }\n\n priorNode () {\n const nodePriorStack = this.nodePriorStack\n const length = nodePriorStack.length\n\n if (length > 1) {\n const parent = nodePriorStack[length - 2]\n const l = parent.length\n return parent[l - 2] || null\n }\n return null\n }\n\n formatDescription (index, formatDescription, useOverrideForNode) {\n const nodeStack = this.nodeStack\n const length = nodeStack.length\n\n index = index || 0\n if (index >= length) { return null }\n\n // Get the nodes backwards from the stack\n const i = length - index - 1\n const currentNode = nodeStack[i]\n const currentFormatDescription = formatDescription || this.compiler.formatDescription\n // Get the parent descriptions except if no formatDescription was provided, then it is the root description\n const parentFormatDescriptions = formatDescription ? formatDescription.parent : currentFormatDescription\n\n let nextFormatDescription\n if (parentFormatDescriptions) {\n const nodeType = useOverrideForNode === currentNode ? this.nodeStackOverrideType[i] : currentNode.type\n // console.log(\"nodeType: \" + nodeType + \", (useOverrideForNode === currentNode):\" + + !!(useOverrideForNode === currentNode));\n nextFormatDescription = parentFormatDescriptions[nodeType]\n if (useOverrideForNode === currentNode && !nextFormatDescription) {\n // console.log(\"Stop\");\n return null\n }\n }\n\n // console.log(\"index: \" + index + \", currentNode: \" + JSON.stringify(currentNode) + \", currentFormatDescription: \" + JSON.stringify(currentFormatDescription) + \", nextFormatDescription: \" + JSON.stringify(nextFormatDescription));\n\n if (nextFormatDescription) {\n // Check for more 'parent' attributes or return nextFormatDescription\n return this.formatDescription(index + 1, nextFormatDescription)\n } else {\n // Check for a virtual node one step up in the stack\n nextFormatDescription = this.formatDescription(index + 1, formatDescription, currentNode)\n if (nextFormatDescription) { return nextFormatDescription } else {\n // Ok, we have found a format description (currentFormatDescription).\n // Lets check if we have any other descriptions dependent on the prior node.\n const priorFormatDescriptions = currentFormatDescription.prior\n if (priorFormatDescriptions) {\n const priorNode = this.priorNode()\n const priorFormatDescription = priorFormatDescriptions[priorNode ? priorNode.type : 'None']\n if (priorFormatDescription) { return priorFormatDescription }\n }\n return currentFormatDescription\n }\n }\n }\n}\n\nexport class BlockScope extends Scope {\n variablesNotReadWarnings () {\n Scope.prototype.variablesNotReadWarnings.call(this)\n\n const prev = this.prev\n\n // Any possible hoisted variable in this scope has to be moved to the previous scope if it is not declared in the previsous scope\n // We can't be top scope. The scope must have some possible hoisted variables\n if (prev && this.possibleHoistedVariables) {\n for (const key in this.possibleHoistedVariables) {\n const possibleHoistedVariable = this.possibleHoistedVariables[key]\n\n if (possibleHoistedVariable) {\n const varInPrevScope = prev.vars && prev.vars[key]\n\n if (varInPrevScope != null) {\n const prevPossibleHoistedVariable = (prev.possibleHoistedVariables || (prev.possibleHoistedVariables = Object.create(null)))[key]\n\n if (prevPossibleHoistedVariable == null) {\n prev.possibleHoistedVariables[key] = possibleHoistedVariable\n } else {\n throw new Error(\"Internal inconsistency, previous scope should not have this possible hoisted variable '\" + key + \"'\")\n }\n }\n }\n }\n }\n }\n}\n\nexport class FunctionScope extends BlockScope {\n getVarScope () {\n return this\n }\n}\n","import { SourceNode } from 'source-map'\n\nexport class StringBuffer {\n constructor (useSourceNode, file, sourceContent) {\n if (useSourceNode) {\n this.rootNode = new SourceNode()\n this.concat = this.concatSourceNode\n this.toString = this.toStringSourceNode\n this.isEmpty = this.isEmptySourceNode\n this.appendStringBuffer = this.appendStringBufferSourceNode\n this.length = this.lengthSourceNode\n this.removeAtIndex = this.removeAtIndexSourceNode\n if (file) {\n const fileString = file.toString()\n const filename = fileString.substr(fileString.lastIndexOf('/') + 1)\n const sourceRoot = fileString.substr(0, fileString.lastIndexOf('/') + 1)\n\n this.filename = filename\n\n if (sourceRoot.length > 0) { this.sourceRoot = sourceRoot }\n if (sourceContent != null) { this.rootNode.setSourceContent(filename, sourceContent) }\n }\n\n if (sourceContent != null) { this.sourceContent = sourceContent }\n } else {\n this.atoms = []\n this.concat = this.concatString\n this.toString = this.toStringString\n this.isEmpty = this.isEmptyString\n this.appendStringBuffer = this.appendStringBufferString\n this.length = this.lengthString\n this.removeAtIndex = this.removeAtIndexString\n }\n }\n\n toStringString () {\n return this.atoms.join('')\n }\n\n toStringSourceNode () {\n return this.rootNode.toStringWithSourceMap({ file: this.filename + 's', sourceRoot: this.sourceRoot })\n }\n\n concatString (aString) {\n this.atoms.push(aString)\n }\n\n concatSourceNode (aString, node, originalName) {\n if (node) {\n // console.log(\"Snippet: \" + aString + \", line: \" + node.loc.start.line + \", column: \" + node.loc.start.column + \", source: \" + node.loc.source);\n this.rootNode.add(new SourceNode(node.loc.start.line, node.loc.start.column, node.loc.source, aString, originalName))\n } else { this.rootNode.add(aString) }\n if (!this.notEmpty) { this.notEmpty = true }\n }\n\n // '\\n' will indent. '\\n\\0' will not indent. '\\n\\1' will indent one more then the current indent level.\n // '\\n\\-1' will indent one less then the current indent level. Numbers from 0-9 can me used.\n concatFormat (aString) {\n if (!aString) return\n const lines = aString.split('\\n')\n const size = lines.length\n if (size > 1) {\n this.concat(lines[0])\n for (let i = 1; i < size; i++) {\n let line = lines[i]\n this.concat('\\n')\n if (line.slice(0, 1) === '\\\\') {\n let numberLength = 1\n let indent = line.slice(1, 1 + numberLength)\n if (indent === '-') {\n numberLength = 2\n indent = line.slice(1, 1 + numberLength)\n }\n const indentationNumber = parseInt(indent)\n if (indentationNumber) {\n this.concat(indentationNumber > 0 ? indentation + Array(indentationNumber * indentationSpaces + 1).join(indentType) : indentation.substring(indentationSize * -indentationNumber))\n }\n line = line.slice(1 + numberLength)\n } else if (line || i === size - 1) {\n // Ident if there is something between line breaks or the last linebreak\n this.concat(this.indentation)\n }\n if (line) this.concat(line)\n }\n } else { this.concat(aString) }\n }\n\n isEmptyString () {\n return this.atoms.length !== 0\n }\n\n isEmptySourceNode () {\n return this.notEmpty\n }\n\n appendStringBufferString (stringBuffer) {\n // We can't do 'this.atoms.push.apply(this.atoms, stringBuffer.atoms);' as JavaScriptCore (WebKit) has a limit on number of arguments at 65536.\n // Other browsers also have simular limits.\n const thisAtoms = this.atoms\n const thisLength = thisAtoms.length\n const stringBufferAtoms = stringBuffer.atoms\n const stringBufferLength = stringBufferAtoms.length\n\n thisAtoms.length = thisLength + stringBufferLength\n\n for (let i = 0; i < stringBufferLength; i++) {\n thisAtoms[thisLength + i] = stringBufferAtoms[i]\n }\n }\n\n appendStringBufferSourceNode (stringBuffer) {\n this.rootNode.add(stringBuffer.rootNode)\n }\n\n lengthString () {\n return this.atoms.length\n }\n\n lengthSourceNode () {\n return this.rootNode.children.length\n }\n\n removeAtIndexString (index) {\n this.atoms[index] = ''\n }\n\n removeAtIndexSourceNode (index) {\n this.rootNode.children[index] = ''\n }\n}\n","import { warningUnusedButSetVariable, warningShadowIvar, warningCreateGlobalInsideFunctionOrMethod, warningUnknownClassOrGlobal, warningUnknownIvarType } from './warning.js'\n\n// A optional argument can be given to further configure\n// the compiler. These options are recognized:\n\nexport const defaultOptions = {\n\n // Acorn options. For more information check objj-acorn.\n // We have a function here to create a new object every time we copy\n // the default options.\n acornOptions: function () { return Object.create(null) },\n\n // Turn on `sourceMap` generate a source map for the compiler file.\n sourceMap: false,\n\n // Turn on `sourceMapIncludeSource` will include the source code in the source map.\n sourceMapIncludeSource: false,\n\n // The compiler can do different passes.\n // 1: Parse and walk AST tree to collect file dependencies.\n // 2: Parse and walk to generate code.\n // Pass one is only for the Objective-J load and runtime.\n pass: 2,\n\n // Pass in class definitions. New class definitions in source file will be added here when compiling.\n classDefs: function () { return Object.create(null) },\n\n // Pass in protocol definitions. New protocol definitions in source file will be added here when compiling.\n protocolDefs: function () { return Object.create(null) },\n\n // Pass in typeDef definitions. New typeDef definitions in source file will be added here when compiling.\n typeDefs: function () { return Object.create(null) },\n\n // Turn off `generate` to make the compile copy the code from the source file (and replace needed parts)\n // instead of generate it from the AST tree. The preprocessor does not work if this is turn off as it alters\n // the AST tree and not the original source. We should deprecate this in the future.\n generate: true,\n\n // Turn on `generateObjJ` to generate Objecitve-J code instead of Javascript code. This can be used to beautify\n // the code.\n generateObjJ: false,\n\n // How many spaces for indentation when generation code.\n indentationSpaces: 4,\n\n // The type of indentation. Default is space. Can be changed to tab or any other string.\n indentationType: ' ',\n\n // There is a bug in Safari 2.0 that can't handle a named function declaration. See http://kangax.github.io/nfe/#safari-bug\n // Turn on `transformNamedFunctionDeclarationToAssignment` to make the compiler transform these.\n // We support this here as the old Objective-J compiler (Not a real compiler, Preprocessor.js) transformed\n // named function declarations to assignments.\n // Example: 'function f(x) { return x }' transforms to: 'f = function(x) { return x }'\n transformNamedFunctionDeclarationToAssignment: false,\n\n // Turn off `includeMethodFunctionNames` to remove function names on methods.\n includeMethodFunctionNames: true,\n\n // Turn off `includeMethodArgumentTypeSignatures` to remove type information on method arguments.\n includeMethodArgumentTypeSignatures: true,\n\n // Turn off `includeIvarTypeSignatures` to remove type information on ivars.\n includeIvarTypeSignatures: true,\n\n // Turn off `inlineMsgSendFunctions` to use message send functions. Needed to use message send decorators.\n inlineMsgSendFunctions: true,\n\n // `warning` includes the warnings that are turned on. It is just used for some warnings.\n warnings: [warningUnusedButSetVariable, warningShadowIvar, warningCreateGlobalInsideFunctionOrMethod, warningUnknownClassOrGlobal, warningUnknownIvarType],\n\n // An array of macro objects and/or text definitions may be passed in.\n // Definitions may be in one of two forms:\n // macro\n // macro=body\n macros: null\n\n}\n\n// We copy the options to a new object as we don't want to mess up incoming options when we start compiling.\nexport function setupOptions (opts) {\n const options = Object.create(null)\n for (const opt in defaultOptions) {\n if (opts && Object.prototype.hasOwnProperty.call(opts, opt)) {\n const incomingOpt = opts[opt]\n options[opt] = typeof incomingOpt === 'function' ? incomingOpt() : incomingOpt\n } else if (Object.prototype.hasOwnProperty.call(defaultOptions, opt)) {\n const defaultOpt = defaultOptions[opt]\n options[opt] = typeof defaultOpt === 'function' ? defaultOpt() : defaultOpt\n }\n }\n return options\n}\n","export class TypeDef {\n constructor (name) {\n this.name = name\n }\n}\n\n// methodDef = {\"types\": types, \"name\": selector}\nexport class MethodDef {\n constructor (name, types) {\n this.name = name\n this.types = types\n }\n}\n","// Both the ClassDef and ProtocolDef conforms to a 'protocol' (That we can't declare in Javascript).\n// Both Objects have the attribute 'protocols': Array of ProtocolDef that they conform to\n// Both also have the functions: addInstanceMethod, addClassMethod, getInstanceMethod and getClassMethod\n// classDef = {\"className\": aClassName, \"superClass\": superClass , \"ivars\": myIvars, \"instanceMethods\": instanceMethodDefs, \"classMethods\": classMethodDefs, \"protocols\": myProtocols};\n\nexport class ClassDef {\n constructor (isImplementationDeclaration, name, superClass, ivars, instanceMethods, classMethods, protocols) {\n this.name = name\n if (superClass) { this.superClass = superClass }\n if (ivars) { this.ivars = ivars }\n if (isImplementationDeclaration) {\n this.instanceMethods = instanceMethods || Object.create(null)\n this.classMethods = classMethods || Object.create(null)\n }\n if (protocols) { this.protocols = protocols }\n }\n\n addInstanceMethod (methodDef) {\n this.instanceMethods[methodDef.name] = methodDef\n }\n\n addClassMethod (methodDef) {\n this.classMethods[methodDef.name] = methodDef\n }\n\n listOfNotImplementedMethodsForProtocols (protocolDefs) {\n let resultList = []\n const instanceMethods = this.getInstanceMethods()\n const classMethods = this.getClassMethods()\n\n for (let i = 0, size = protocolDefs.length; i < size; i++) {\n const protocolDef = protocolDefs[i]\n const protocolInstanceMethods = protocolDef.requiredInstanceMethods\n const protocolClassMethods = protocolDef.requiredClassMethods\n const inheritFromProtocols = protocolDef.protocols\n\n if (protocolInstanceMethods) {\n for (const methodName in protocolInstanceMethods) {\n const methodDef = protocolInstanceMethods[methodName]\n if (!instanceMethods[methodName]) resultList.push({ methodDef, protocolDef })\n }\n }\n\n if (protocolClassMethods) {\n for (const methodName in protocolClassMethods) {\n const methodDef = protocolClassMethods[methodName]\n if (!classMethods[methodName]) resultList.push({ methodDef, protocolDef })\n }\n }\n\n if (inheritFromProtocols) { resultList = resultList.concat(this.listOfNotImplementedMethodsForProtocols(inheritFromProtocols)) }\n }\n\n return resultList\n }\n\n getInstanceMethod (name) {\n const instanceMethods = this.instanceMethods\n\n if (instanceMethods) {\n const method = instanceMethods[name]\n\n if (method) { return method }\n }\n\n const superClass = this.superClass\n\n if (superClass) { return superClass.getInstanceMethod(name) }\n\n return null\n }\n\n getClassMethod (name) {\n const classMethods = this.classMethods\n if (classMethods) {\n const method = classMethods[name]\n\n if (method) { return method }\n }\n\n const superClass = this.superClass\n\n if (superClass) { return superClass.getClassMethod(name) }\n\n return null\n }\n\n // Return a new Array with all instance methods\n getInstanceMethods () {\n const instanceMethods = this.instanceMethods\n if (instanceMethods) {\n const superClass = this.superClass\n const returnObject = Object.create(null)\n if (superClass) {\n const superClassMethods = superClass.getInstanceMethods()\n for (const methodName in superClassMethods) { returnObject[methodName] = superClassMethods[methodName] }\n }\n\n for (const methodName in instanceMethods) { returnObject[methodName] = instanceMethods[methodName] }\n\n return returnObject\n }\n\n return []\n }\n\n // Return a new Array with all class methods\n getClassMethods () {\n const classMethods = this.classMethods\n if (classMethods) {\n const superClass = this.superClass\n const returnObject = Object.create(null)\n if (superClass) {\n const superClassMethods = superClass.getClassMethods()\n for (const methodName in superClassMethods) { returnObject[methodName] = superClassMethods[methodName] }\n }\n\n for (const methodName in classMethods) { returnObject[methodName] = classMethods[methodName] }\n\n return returnObject\n }\n\n return []\n }\n}\n","// Both the ClassDef and ProtocolDef conforms to a 'protocol' (That we can't declare in Javascript).\n// Both Objects have the attribute 'protocols': Array of ProtocolDef that they conform to\n// Both also have the functions: addInstanceMethod, addClassMethod, getInstanceMethod and getClassMethod\n// protocolDef = {\"name\": aProtocolName, \"protocols\": inheritFromProtocols, \"requiredInstanceMethods\": requiredInstanceMethodDefs, \"requiredClassMethods\": requiredClassMethodDefs};\n\nexport class ProtocolDef {\n constructor (name, protocols, requiredInstanceMethodDefs, requiredClassMethodDefs) {\n this.name = name\n this.protocols = protocols\n if (requiredInstanceMethodDefs) { this.requiredInstanceMethods = requiredInstanceMethodDefs }\n if (requiredClassMethodDefs) { this.requiredClassMethods = requiredClassMethodDefs }\n }\n\n addInstanceMethod = function (methodDef) {\n (this.requiredInstanceMethods || (this.requiredInstanceMethods = Object.create(null)))[methodDef.name] = methodDef\n }\n\n addClassMethod = function (methodDef) {\n (this.requiredClassMethods || (this.requiredClassMethods = Object.create(null)))[methodDef.name] = methodDef\n }\n\n getInstanceMethod = function (name) {\n const instanceMethods = this.requiredInstanceMethods\n\n if (instanceMethods) {\n const method = instanceMethods[name]\n\n if (method) { return method }\n }\n\n const protocols = this.protocols\n\n for (let i = 0, size = protocols.length; i < size; i++) {\n const protocol = protocols[i]\n const method = protocol.getInstanceMethod(name)\n\n if (method) { return method }\n }\n\n return null\n }\n\n getClassMethod = function (name) {\n const classMethods = this.requiredClassMethods\n\n if (classMethods) {\n const method = classMethods[name]\n\n if (method) { return method }\n }\n\n const protocols = this.protocols\n\n for (let i = 0, size = protocols.length; i < size; i++) {\n const protocol = protocols[i]\n const method = protocol.getClassMethod(name)\n\n if (method) { return method }\n }\n\n return null\n }\n}\n","export function wordsRegexp (words) {\n return new RegExp('^(?:' + words.replace(/ /g, '|') + ')$')\n}\n","import walk from 'acorn-walk'\nimport { getLineInfo } from 'objj-parser'\nimport { SourceNode, SourceMapConsumer } from 'source-map'\n\nimport { Scope, FunctionScope, BlockScope } from './scope'\nimport { TypeDef, MethodDef } from './definition'\nimport { ClassDef } from './class-def'\nimport { ProtocolDef } from './protocol'\nimport { StringBuffer } from './buffer'\nimport { GlobalVariableMaybeWarning, createMessage, warningUnknownClassOrGlobal, warningCreateGlobalInsideFunctionOrMethod, warningShadowIvar, warningUnknownIvarType } from './warning'\nimport { wordsRegexp } from './util'\nimport { setupOptions } from './options'\n\nfunction isIdempotentExpression (node) {\n switch (node.type) {\n case 'Literal':\n case 'Identifier':\n return true\n\n case 'ArrayExpression':\n for (let i = 0; i < node.elements.length; ++i) {\n if (!isIdempotentExpression(node.elements[i])) { return false }\n }\n\n return true\n\n case 'DictionaryLiteral':\n for (let i = 0; i < node.keys.length; ++i) {\n if (!isIdempotentExpression(node.keys[i])) { return false }\n if (!isIdempotentExpression(node.values[i])) { return false }\n }\n\n return true\n\n case 'ObjectExpression':\n for (let i = 0; i < node.properties.length; ++i) {\n if (!isIdempotentExpression(node.properties[i].value)) { return false }\n }\n\n return true\n\n case 'FunctionExpression':\n for (let i = 0; i < node.params.length; ++i) {\n if (!isIdempotentExpression(node.params[i])) { return false }\n }\n\n return true\n\n case 'SequenceExpression':\n for (let i = 0; i < node.expressions.length; ++i) {\n if (!isIdempotentExpression(node.expressions[i])) { return false }\n }\n\n return true\n\n case 'UnaryExpression':\n return isIdempotentExpression(node.argument)\n\n case 'BinaryExpression':\n return isIdempotentExpression(node.left) && isIdempotentExpression(node.right)\n\n case 'ConditionalExpression':\n return isIdempotentExpression(node.test) && isIdempotentExpression(node.consequent) && isIdempotentExpression(node.alternate)\n\n case 'MemberExpression':\n return isIdempotentExpression(node.object) && (!node.computed || isIdempotentExpression(node.property))\n\n case 'Dereference':\n return isIdempotentExpression(node.expr)\n\n case 'Reference':\n return isIdempotentExpression(node.element)\n\n default:\n return false\n }\n}\n\n// We do not allow dereferencing of expressions with side effects because we might need to evaluate the expression twice in certain uses of deref, which is not obvious when you look at the deref operator in plain code.\nfunction checkCanDereference (st, node) {\n if (!isIdempotentExpression(node)) { throw st.compiler.error_message('Dereference of expression with side effects', node) }\n}\n\n// Surround expression with parentheses\nfunction surroundExpression (c) {\n return function (node, st, override) {\n st.compiler.jsBuffer.concat('(')\n c(node, st, override)\n st.compiler.jsBuffer.concat(')')\n }\n}\n\nconst operatorPrecedence = {\n // MemberExpression\n // These two are never used as they are a MemberExpression with the attribute 'computed' which tells what operator it uses.\n // \".\": 0, \"[]\": 0,\n // NewExpression\n // This is never used.\n // \"new\": 1,\n // All these are UnaryExpression or UpdateExpression and never used.\n // \"!\": 2, \"~\": 2, \"-\": 2, \"+\": 2, \"++\": 2, \"--\": 2, \"typeof\": 2, \"void\": 2, \"delete\": 2,\n // BinaryExpression\n '*': 3,\n '/': 3,\n '%': 3,\n '+': 4,\n '-': 4,\n '<<': 5,\n '>>': 5,\n '>>>': 5,\n '<': 6,\n '<=': 6,\n '>': 6,\n '>=': 6,\n in: 6,\n instanceof: 6,\n '==': 7,\n '!=': 7,\n '===': 7,\n '!==': 7,\n '&': 8,\n '^': 9,\n '|': 10,\n // LogicalExpression\n '&&': 11,\n '||': 12,\n '??': 13\n // ConditionalExpression\n // AssignmentExpression\n}\n\nconst expressionTypePrecedence = {\n MemberExpression: 1,\n CallExpression: 1,\n NewExpression: 1,\n ChainExpression: 2,\n FunctionExpression: 3,\n ArrowFunctionExpression: 3,\n ImportExpression: 3,\n UnaryExpression: 4,\n UpdateExpression: 4,\n BinaryExpression: 5,\n LogicalExpression: 6,\n ConditionalExpression: 7,\n AssignmentExpression: 8\n}\n\nfunction ignore (_node, _st, _c) { }\n\nexport const pass1 = walk.make({\n ImportStatement: function (node, st, c) {\n const urlString = node.filename.value\n\n st.compiler.dependencies.push({ url: urlString, isLocal: node.localfilepath })\n // st.compiler.dependencies.push(typeof FileDependency !== 'undefined' ? new FileDependency(typeof CFURL !== 'undefined' ? new CFURL(urlString) : urlString, node.localfilepath) : urlString);\n },\n TypeDefStatement: ignore,\n ClassStatement: ignore,\n ClassDeclarationStatement: ignore,\n MessageSendExpression: ignore,\n GlobalStatement: ignore,\n ProtocolDeclarationStatement: ignore,\n ArrayLiteral: ignore,\n Reference: ignore,\n DictionaryLiteral: ignore,\n Dereference: ignore,\n SelectorLiteralExpression: ignore\n})\n\n// Returns true if subNode has higher precedence the the root node.\n// If the subNode is the right (as in left/right) subNode\nfunction nodePrecedence (node, subNode, right) {\n const nodeType = node.type\n const nodePrecedence = expressionTypePrecedence[nodeType] || -1\n const subNodePrecedence = expressionTypePrecedence[subNode.type] || -1\n let nodeOperatorPrecedence\n let subNodeOperatorPrecedence\n return nodePrecedence < subNodePrecedence || (nodePrecedence === subNodePrecedence && isLogicalBinary.test(nodeType) && ((nodeOperatorPrecedence = operatorPrecedence[node.operator]) < (subNodeOperatorPrecedence = operatorPrecedence[subNode.operator]) || (right && nodeOperatorPrecedence === subNodeOperatorPrecedence)))\n}\n\n// Used for arrow functions. Checks if the parameter list needs parentheses.\nfunction mustHaveParentheses (paramList) {\n for (const param of paramList) {\n if (param.type !== 'Identifier') {\n return true\n }\n }\n return paramList.length > 1 || paramList.length === 0\n}\n\nconst reservedIdentifiers = wordsRegexp('self _cmd __filename undefined localStorage arguments')\nconst wordPrefixOperators = wordsRegexp('delete in instanceof new typeof void')\nconst isLogicalBinary = wordsRegexp('LogicalExpression BinaryExpression')\n\nexport const pass2 = walk.make({\n Program: function (node, st, c) {\n for (let i = 0; i < node.body.length; ++i) {\n c(node.body[i], st, 'Statement')\n }\n\n // Check maybe warnings\n const maybeWarnings = st.maybeWarnings()\n if (maybeWarnings) {\n for (let i = 0; i < maybeWarnings.length; i++) {\n const maybeWarning = maybeWarnings[i]\n if (maybeWarning.checkIfWarning(st)) {\n st.compiler.addWarning(maybeWarning.message)\n }\n }\n }\n },\n BlockStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n\n const isDecl = st.isDecl\n if (isDecl != null) {\n delete st.isDecl\n }\n\n const endOfScopeBody = st.endOfScopeBody\n if (endOfScopeBody) {\n delete st.endOfScopeBody\n }\n\n const skipIndentation = st.skipIndentation\n if (skipIndentation) {\n delete st.skipIndentation\n } else {\n buffer.concat(compiler.indentation.substring(compiler.indentationSize))\n }\n\n buffer.concat('{\\n', node)\n const inner = endOfScopeBody ? st : new BlockScope(st)\n for (let i = 0; i < node.body.length; ++i) {\n if (node.body[i].type === 'BlockStatement') {\n compiler.indentation += compiler.indentStep\n c(node.body[i], inner, 'Statement')\n compiler.indentation = compiler.indentation.substring(compiler.indentationSize)\n } else {\n c(node.body[i], inner, 'Statement')\n }\n }\n !endOfScopeBody && inner.variablesNotReadWarnings()\n const maxReceiverLevel = st.maxReceiverLevel\n if (endOfScopeBody && maxReceiverLevel) {\n buffer.concat(compiler.indentation)\n buffer.concat('var ')\n for (let i = 0; i < maxReceiverLevel; i++) {\n if (i) buffer.concat(', ')\n buffer.concat('___r')\n buffer.concat((i + 1) + '')\n }\n buffer.concat(';\\n')\n }\n\n // Simulate a node for the last curly bracket\n // var endNode = node.loc && { loc: { start: { line : node.loc.end.line, column: node.loc.end.column}}, source: node.loc.source};\n buffer.concat(compiler.indentation.substring(compiler.indentationSize))\n buffer.concat('}', node)\n if (st.isDefaultExport) buffer.concat(';')\n if (!skipIndentation && isDecl !== false) {\n buffer.concat('\\n')\n }\n st.indentBlockLevel--\n },\n ExpressionStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n if (node.expression.type === 'Reference') throw compiler.error_message(\"Can't have reference of expression as a statement\", node.expression)\n if ((node.expression.type === 'AssignmentExpression' && node.expression.left.type === 'ObjectPattern') || node.expression.type === 'FunctionExpression' || node.expression.type === 'ObjectExpression' || (node.expression.type === 'BinaryExpression' && node.expression.left.type === 'FunctionExpression') || (node.expression.type === 'Literal' && node.expression.value === 'use strict' && !node.directive)) {\n surroundExpression(c)(node.expression, st, 'Expression')\n } else {\n c(node.expression, st, 'Expression')\n }\n buffer.concat(';\\n', node)\n },\n IfStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n // Keep the 'else' and 'if' on the same line if it is an 'else if'\n if (!st.superNodeIsElse) { buffer.concat(st.compiler.indentation) } else { delete st.superNodeIsElse }\n buffer.concat('if (', node)\n c(node.test, st, 'Expression')\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(')')\n if (node.consequent.type !== 'EmptyStatement') buffer.concat('\\n')\n st.compiler.indentation += st.compiler.indentStep\n c(node.consequent, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n const alternate = node.alternate\n if (alternate) {\n const alternateNotIf = alternate.type !== 'IfStatement'\n const emptyStatement = alternate.type === 'EmptyStatement'\n buffer.concat(st.compiler.indentation)\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(alternateNotIf ? emptyStatement ? 'else' : 'else\\n' : 'else ', node)\n if (alternateNotIf) { st.compiler.indentation += st.compiler.indentStep } else { st.superNodeIsElse = true }\n\n c(alternate, st, 'Statement')\n if (alternateNotIf) st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n }\n },\n LabeledStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n c(node.label, st, 'VariablePattern')\n buffer.concat(': ', node)\n c(node.body, st, 'Statement')\n },\n BreakStatement: function (node, st, c) {\n const compiler = st.compiler\n const label = node.label\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n if (label) {\n buffer.concat('break ', node)\n c(label, st, 'VariablePattern')\n buffer.concat(';\\n')\n } else { buffer.concat('break;\\n', node) }\n },\n ContinueStatement: function (node, st, c) {\n const compiler = st.compiler\n const label = node.label\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n if (label) {\n buffer.concat('continue ', node)\n c(label, st, 'VariablePattern')\n buffer.concat(';\\n')\n } else { buffer.concat('continue;\\n', node) }\n },\n WithStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('with(', node)\n c(node.object, st, 'Expression')\n buffer.concat(')\\n', node)\n st.compiler.indentation += st.compiler.indentStep\n c(node.body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n },\n SwitchStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('switch(', node)\n c(node.discriminant, st, 'Expression')\n buffer.concat(') {\\n')\n st.compiler.indentation += st.compiler.indentStep\n for (let i = 0; i < node.cases.length; ++i) {\n const cs = node.cases[i]\n if (cs.test) {\n buffer.concat(st.compiler.indentation)\n buffer.concat('case ')\n c(cs.test, st, 'Expression')\n buffer.concat(':\\n')\n } else { buffer.concat('default:\\n') }\n st.compiler.indentation += st.compiler.indentStep\n for (let j = 0; j < cs.consequent.length; ++j) { c(cs.consequent[j], st, 'Statement') }\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n }\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n buffer.concat(st.compiler.indentation)\n buffer.concat('}\\n')\n },\n ReturnStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('return', node)\n if (node.argument) {\n buffer.concat(' ')\n c(node.argument, st, 'Expression')\n }\n buffer.concat(';\\n')\n },\n ThrowStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('throw', node)\n buffer.concat(' ')\n c(node.argument, st, 'Expression')\n buffer.concat(';\\n')\n },\n TryStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('try', node)\n buffer.concat(' ')\n st.compiler.indentation += st.compiler.indentStep\n st.skipIndentation = true\n c(node.block, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n if (node.handler) {\n const handler = node.handler\n const inner = new Scope(st)\n const param = handler.param\n const name = param?.name\n if (name) inner.vars[name] = { type: 'catch clause', node: param }\n buffer.concat('\\n')\n buffer.concat(st.compiler.indentation)\n buffer.concat('catch')\n if (param) {\n buffer.concat('(')\n c(param, st, 'Pattern')\n buffer.concat(') ')\n }\n st.compiler.indentation += st.compiler.indentStep\n inner.skipIndentation = true\n inner.endOfScopeBody = true\n c(handler.body, inner, 'BlockStatement')\n inner.variablesNotReadWarnings()\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n inner.copyAddedSelfToIvarsToParent()\n }\n if (node.finalizer) {\n buffer.concat('\\n')\n buffer.concat(st.compiler.indentation)\n buffer.concat('finally ')\n st.compiler.indentation += st.compiler.indentStep\n st.skipIndentation = true\n c(node.finalizer, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n }\n buffer.concat('\\n')\n },\n WhileStatement: function (node, st, c) {\n const compiler = st.compiler\n const body = node.body\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('while (', node)\n c(node.test, st, 'Expression')\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(')')\n if (node.body.type !== 'EmptyStatement') buffer.concat('\\n')\n st.compiler.indentation += st.compiler.indentStep\n c(body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n },\n DoWhileStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('do\\n', node)\n st.compiler.indentation += st.compiler.indentStep\n c(node.body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n buffer.concat(st.compiler.indentation)\n buffer.concat('while (')\n c(node.test, st, 'Expression')\n buffer.concat(');\\n')\n },\n ForStatement: function (node, st, c) {\n const compiler = st.compiler\n const body = node.body\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('for (', node)\n if (node.init) c(node.init, st, 'ForInit')\n buffer.concat('; ')\n if (node.test) c(node.test, st, 'Expression')\n buffer.concat('; ')\n if (node.update) c(node.update, st, 'Expression')\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(')')\n if (node.body.type !== 'EmptyStatement') buffer.concat('\\n')\n st.compiler.indentation += st.compiler.indentStep\n c(body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n },\n ForInStatement: function (node, st, c) {\n const compiler = st.compiler\n const body = node.body\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('for (', node)\n c(node.left, st, 'ForInit')\n buffer.concat(' in ')\n c(node.right, st, 'Expression')\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(body.type === 'EmptyStatement' ? ')\\n' : ')\\n')\n st.compiler.indentation += st.compiler.indentStep\n c(body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n },\n ForOfStatement: function (node, st, c) { // TODO: Fix code duplication with 'for in'-\n const compiler = st.compiler\n const body = node.body\n const buffer = compiler.jsBuffer\n buffer.concat('for', node)\n if (node.await) buffer.concat(' await ')\n buffer.concat('(')\n c(node.left, st, 'ForInit')\n buffer.concat(' of ')\n c(node.right, st, 'Expression')\n // We don't want EmptyStatements to generate an extra parenthesis except when it is in a while, for, ...\n buffer.concat(body.type === 'EmptyStatement' ? ')\\n' : ')\\n')\n st.compiler.indentation += st.compiler.indentStep\n c(body, st, 'Statement')\n st.compiler.indentation = st.compiler.indentation.substring(st.compiler.indentationSize)\n },\n ForInit: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n if (node.type === 'VariableDeclaration') {\n st.isFor = true\n c(node, st)\n delete st.isFor\n } else if (node.type === 'BinaryExpression' && node.operator === 'in') {\n buffer.concat('(')\n c(node, st, 'Expression')\n buffer.concat(')')\n } else {\n c(node, st, 'Expression')\n }\n },\n DebuggerStatement: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n buffer.concat(st.compiler.indentation)\n buffer.concat('debugger;\\n', node)\n },\n Function: function (node, st, c) {\n const compiler = st.compiler\n const buffer = compiler.jsBuffer\n const inner = new FunctionScope(st)\n const decl = node.type === 'FunctionDeclaration'\n const id = node.id\n\n inner.isDecl = decl\n for (let i = 0; i < node.params.length; ++i) { inner.vars[node.params[i].name] = { type: 'argument', node: node.params[i] } }\n buffer.concat(st.compiler.indentation)\n if (id) {\n const name = id.name;\n (decl ? st : inner).vars[name] = { type: decl ? 'function' : 'function name', node: id }\n if (!st.skipFunctionKeyword && compiler.transformNamedFunctionDeclarationToAssignment) {\n buffer.concat(name)\n buffer.concat(' = ')\n }\n }\n if (st.isDefaultExport && !decl) buffer.concat('(')\n const prefix = []\n if (st.methodPrefix?.length) {\n prefix.push(...st.methodPrefix)\n }\n if (node.async) prefix.push('async')\n if (!st.skipFunctionKeyword) {\n prefix.push('function')\n }\n if (node.generator) prefix.push('*')\n buffer.concat(prefix.join(' '))\n if ((st.skipFunctionKeyword || !compiler.transformNamedFunctionDeclarationToAssignment) && id) {\n buffer.concat(' ')\n if (st.isComputed) buffer.concat('[')\n c(id, st)\n if (st.isComputed) buffer.concat(']')\n }\n buffer.concat('(')\n for (let i = 0; i < node.params.length; ++i) {\n if (i) { buffer.concat(', ') }\n if (node.params[i].type === 'RestElement') {\n c(node.params[i], st, 'RestElement')\n } e