summaryrefslogtreecommitdiffstats
path: root/devtools/client/sourceeditor/tern/def.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/sourceeditor/tern/def.js')
-rwxr-xr-xdevtools/client/sourceeditor/tern/def.js656
1 files changed, 656 insertions, 0 deletions
diff --git a/devtools/client/sourceeditor/tern/def.js b/devtools/client/sourceeditor/tern/def.js
new file mode 100755
index 000000000..71f6e7991
--- /dev/null
+++ b/devtools/client/sourceeditor/tern/def.js
@@ -0,0 +1,656 @@
+// Type description parser
+//
+// Type description JSON files (such as ecma5.json and browser.json)
+// are used to
+//
+// A) describe types that come from native code
+//
+// B) to cheaply load the types for big libraries, or libraries that
+// can't be inferred well
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ return exports.init = mod;
+ if (typeof define == "function" && define.amd) // AMD
+ return define({init: mod});
+ tern.def = {init: mod};
+})(function(exports, infer) {
+ "use strict";
+
+ function hop(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+ }
+
+ var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) {
+ this.pos = start || 0;
+ this.spec = spec;
+ this.base = base;
+ this.forceNew = forceNew;
+ };
+
+ function unwrapType(type, self, args) {
+ return type.call ? type(self, args) : type;
+ }
+
+ function extractProp(type, prop) {
+ if (prop == "!ret") {
+ if (type.retval) return type.retval;
+ var rv = new infer.AVal;
+ type.propagate(new infer.IsCallee(infer.ANull, [], null, rv));
+ return rv;
+ } else {
+ return type.getProp(prop);
+ }
+ }
+
+ function computedFunc(name, args, retType, generator) {
+ return function(self, cArgs) {
+ var realArgs = [];
+ for (var i = 0; i < args.length; i++) realArgs.push(unwrapType(args[i], self, cArgs));
+ return new infer.Fn(name, infer.ANull, realArgs, unwrapType(retType, self, cArgs), generator);
+ };
+ }
+ function computedUnion(types) {
+ return function(self, args) {
+ var union = new infer.AVal;
+ for (var i = 0; i < types.length; i++) unwrapType(types[i], self, args).propagate(union);
+ union.maxWeight = 1e5;
+ return union;
+ };
+ }
+ function computedArray(inner) {
+ return function(self, args) {
+ return new infer.Arr(inner(self, args));
+ };
+ }
+ function computedTuple(types) {
+ return function(self, args) {
+ return new infer.Arr(types.map(function(tp) { return unwrapType(tp, self, args) }))
+ }
+ }
+
+ TypeParser.prototype = {
+ eat: function(str) {
+ if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) {
+ this.pos += str.length;
+ return true;
+ }
+ },
+ word: function(re) {
+ var word = "", ch, re = re || /[\w$]/;
+ while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; }
+ return word;
+ },
+ error: function() {
+ throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")");
+ },
+ parseFnType: function(comp, name, top, generator) {
+ var args = [], names = [], computed = false;
+ if (!this.eat(")")) for (var i = 0; ; ++i) {
+ var colon = this.spec.indexOf(": ", this.pos), argname;
+ if (colon != -1) {
+ argname = this.spec.slice(this.pos, colon);
+ if (/^[$\w?]+$/.test(argname))
+ this.pos = colon + 2;
+ else
+ argname = null;
+ }
+ names.push(argname);
+ var argType = this.parseType(comp);
+ if (argType.call) computed = true;
+ args.push(argType);
+ if (!this.eat(", ")) {
+ this.eat(")") || this.error();
+ break;
+ }
+ }
+ var retType, computeRet, computeRetStart, fn;
+ if (this.eat(" -> ")) {
+ var retStart = this.pos;
+ retType = this.parseType(true);
+ if (retType.call && !computed) {
+ computeRet = retType;
+ retType = infer.ANull;
+ computeRetStart = retStart;
+ }
+ } else {
+ retType = infer.ANull;
+ }
+ if (computed) return computedFunc(name, args, retType, generator);
+
+ if (top && (fn = this.base))
+ infer.Fn.call(this.base, name, infer.ANull, args, names, retType, generator);
+ else
+ fn = new infer.Fn(name, infer.ANull, args, names, retType, generator);
+ if (computeRet) fn.computeRet = computeRet;
+ if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos);
+ return fn;
+ },
+ parseType: function(comp, name, top) {
+ var main = this.parseTypeMaybeProp(comp, name, top);
+ if (!this.eat("|")) return main;
+ var types = [main], computed = main.call;
+ for (;;) {
+ var next = this.parseTypeMaybeProp(comp, name, top);
+ types.push(next);
+ if (next.call) computed = true;
+ if (!this.eat("|")) break;
+ }
+ if (computed) return computedUnion(types);
+ var union = new infer.AVal;
+ for (var i = 0; i < types.length; i++) types[i].propagate(union);
+ union.maxWeight = 1e5;
+ return union;
+ },
+ parseTypeMaybeProp: function(comp, name, top) {
+ var result = this.parseTypeInner(comp, name, top);
+ while (comp && this.eat(".")) result = this.extendWithProp(result);
+ return result;
+ },
+ extendWithProp: function(base) {
+ var propName = this.word(/[\w<>$!:]/) || this.error();
+ if (base.apply) return function(self, args) {
+ return extractProp(base(self, args), propName);
+ };
+ return extractProp(base, propName);
+ },
+ parseTypeInner: function(comp, name, top) {
+ var gen
+ if (this.eat("fn(") || (gen = this.eat("fn*("))) {
+ return this.parseFnType(comp, name, top, gen);
+ } else if (this.eat("[")) {
+ var inner = this.parseType(comp), types, computed = inner.call
+ while (this.eat(", ")) {
+ if (!types) types = [inner]
+ var next = this.parseType(comp)
+ types.push(next)
+ computed = computed || next.call
+ }
+ this.eat("]") || this.error()
+ if (computed) return types ? computedTuple(types) : computedArray(inner)
+ if (top && this.base) {
+ infer.Arr.call(this.base, types || inner)
+ return this.base
+ }
+ return new infer.Arr(types || inner)
+ } else if (this.eat("+")) {
+ var path = this.word(/[\w$<>\.:!]/)
+ var base = infer.cx().localDefs[path + ".prototype"]
+ if (!base) {
+ var base = parsePath(path);
+ if (!(base instanceof infer.Obj)) return base;
+ var proto = descendProps(base, ["prototype"])
+ if (proto && (proto = proto.getObjType()))
+ base = proto
+ }
+ if (comp && this.eat("[")) return this.parsePoly(base);
+ if (top && this.forceNew) return new infer.Obj(base);
+ return infer.getInstance(base);
+ } else if (this.eat(":")) {
+ var name = this.word(/[\w$\.]/)
+ return infer.getSymbol(name)
+ } else if (comp && this.eat("!")) {
+ var arg = this.word(/\d/);
+ if (arg) {
+ arg = Number(arg);
+ return function(_self, args) {return args[arg] || infer.ANull;};
+ } else if (this.eat("this")) {
+ return function(self) {return self;};
+ } else if (this.eat("custom:")) {
+ var fname = this.word(/[\w$]/);
+ return customFunctions[fname] || function() { return infer.ANull; };
+ } else {
+ return this.fromWord("!" + this.word(/[\w$<>\.!:]/));
+ }
+ } else if (this.eat("?")) {
+ return infer.ANull;
+ } else {
+ return this.fromWord(this.word(/[\w$<>\.!:`]/));
+ }
+ },
+ fromWord: function(spec) {
+ var cx = infer.cx();
+ switch (spec) {
+ case "number": return cx.num;
+ case "string": return cx.str;
+ case "bool": return cx.bool;
+ case "<top>": return cx.topScope;
+ }
+ if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec];
+ return parsePath(spec);
+ },
+ parsePoly: function(base) {
+ var propName = "<i>", match;
+ if (match = this.spec.slice(this.pos).match(/^\s*([\w$:]+)\s*=\s*/)) {
+ propName = match[1];
+ this.pos += match[0].length;
+ }
+ var value = this.parseType(true);
+ if (!this.eat("]")) this.error();
+ if (value.call) return function(self, args) {
+ var instance = new infer.Obj(base);
+ value(self, args).propagate(instance.defProp(propName));
+ return instance;
+ };
+ var instance = new infer.Obj(base);
+ value.propagate(instance.defProp(propName));
+ return instance;
+ }
+ };
+
+ function parseType(spec, name, base, forceNew) {
+ var type = new TypeParser(spec, null, base, forceNew).parseType(false, name, true);
+ if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) {
+ var arg = type.args[i];
+ if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) {
+ var fArg = fArgs[i];
+ if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull));
+ });
+ })(i);
+ return type;
+ }
+
+ function addEffect(fn, handler, replaceRet) {
+ var oldCmp = fn.computeRet, rv = fn.retval;
+ fn.computeRet = function(self, args, argNodes) {
+ var handled = handler(self, args, argNodes);
+ var old = oldCmp ? oldCmp(self, args, argNodes) : rv;
+ return replaceRet ? handled : old;
+ };
+ }
+
+ var parseEffect = exports.parseEffect = function(effect, fn) {
+ var m;
+ if (effect.indexOf("propagate ") == 0) {
+ var p = new TypeParser(effect, 10);
+ var origin = p.parseType(true);
+ if (!p.eat(" ")) p.error();
+ var target = p.parseType(true);
+ addEffect(fn, function(self, args) {
+ unwrapType(origin, self, args).propagate(unwrapType(target, self, args));
+ });
+ } else if (effect.indexOf("call ") == 0) {
+ var andRet = effect.indexOf("and return ", 5) == 5;
+ var p = new TypeParser(effect, andRet ? 16 : 5);
+ var getCallee = p.parseType(true), getSelf = null, getArgs = [];
+ if (p.eat(" this=")) getSelf = p.parseType(true);
+ while (p.eat(" ")) getArgs.push(p.parseType(true));
+ addEffect(fn, function(self, args) {
+ var callee = unwrapType(getCallee, self, args);
+ var slf = getSelf ? unwrapType(getSelf, self, args) : infer.ANull, as = [];
+ for (var i = 0; i < getArgs.length; ++i) as.push(unwrapType(getArgs[i], self, args));
+ var result = andRet ? new infer.AVal : infer.ANull;
+ callee.propagate(new infer.IsCallee(slf, as, null, result));
+ return result;
+ }, andRet);
+ } else if (m = effect.match(/^custom (\S+)\s*(.*)/)) {
+ var customFunc = customFunctions[m[1]];
+ if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc);
+ } else if (effect.indexOf("copy ") == 0) {
+ var p = new TypeParser(effect, 5);
+ var getFrom = p.parseType(true);
+ p.eat(" ");
+ var getTo = p.parseType(true);
+ addEffect(fn, function(self, args) {
+ var from = unwrapType(getFrom, self, args), to = unwrapType(getTo, self, args);
+ from.forAllProps(function(prop, val, local) {
+ if (local && prop != "<i>")
+ to.propagate(new infer.DefProp(prop, val));
+ });
+ });
+ } else {
+ throw new Error("Unknown effect type: " + effect);
+ }
+ };
+
+ var currentTopScope;
+
+ var parsePath = exports.parsePath = function(path, scope) {
+ var cx = infer.cx(), cached = cx.paths[path], origPath = path;
+ if (cached != null) return cached;
+ cx.paths[path] = infer.ANull;
+
+ var base = scope || currentTopScope || cx.topScope;
+
+ if (cx.localDefs) for (var name in cx.localDefs) {
+ if (path.indexOf(name) == 0) {
+ if (path == name) return cx.paths[path] = cx.localDefs[path];
+ if (path.charAt(name.length) == ".") {
+ base = cx.localDefs[name];
+ path = path.slice(name.length + 1);
+ break;
+ }
+ }
+ }
+
+ var result = descendProps(base, path.split("."))
+ // Uncomment this to get feedback on your poorly written .json files
+ // if (result == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")")
+ cx.paths[origPath] = result == infer.ANull ? null : result
+ return result
+ }
+
+ function descendProps(base, parts) {
+ for (var i = 0; i < parts.length && base != infer.ANull; ++i) {
+ var prop = parts[i];
+ if (prop.charAt(0) == "!") {
+ if (prop == "!proto") {
+ base = (base instanceof infer.Obj && base.proto) || infer.ANull;
+ } else {
+ var fn = base.getFunctionType();
+ if (!fn) {
+ base = infer.ANull;
+ } else if (prop == "!ret") {
+ base = fn.retval && fn.retval.getType(false) || infer.ANull;
+ } else {
+ var arg = fn.args && fn.args[Number(prop.slice(1))];
+ base = (arg && arg.getType(false)) || infer.ANull;
+ }
+ }
+ } else if (base instanceof infer.Obj) {
+ var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop];
+ if (!propVal || propVal.isEmpty())
+ base = infer.ANull;
+ else
+ base = propVal.types[0];
+ }
+ }
+ return base;
+ }
+
+ function emptyObj(ctor) {
+ var empty = Object.create(ctor.prototype);
+ empty.props = Object.create(null);
+ empty.isShell = true;
+ return empty;
+ }
+
+ function isSimpleAnnotation(spec) {
+ if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false;
+ for (var prop in spec)
+ if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
+ return false;
+ return true;
+ }
+
+ function passOne(base, spec, path) {
+ if (!base) {
+ var tp = spec["!type"];
+ if (tp) {
+ if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn);
+ else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr);
+ else throw new Error("Invalid !type spec: " + tp);
+ } else if (spec["!stdProto"]) {
+ base = infer.cx().protos[spec["!stdProto"]];
+ } else {
+ base = emptyObj(infer.Obj);
+ }
+ base.name = path;
+ }
+
+ for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
+ var inner = spec[name];
+ if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
+ var prop = base.defProp(name);
+ passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop);
+ }
+ return base;
+ }
+
+ function passTwo(base, spec, path) {
+ if (base.isShell) {
+ delete base.isShell;
+ var tp = spec["!type"];
+ if (tp) {
+ parseType(tp, path, base);
+ } else {
+ var proto = spec["!proto"] && parseType(spec["!proto"]);
+ infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path);
+ }
+ }
+
+ var effects = spec["!effects"];
+ if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i)
+ parseEffect(effects[i], base);
+ copyInfo(spec, base);
+
+ for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
+ var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
+ if (typeof inner == "string") {
+ if (known.isEmpty()) parseType(inner, innerPath).propagate(known);
+ } else {
+ if (!isSimpleAnnotation(inner))
+ passTwo(known.getObjType(), inner, innerPath);
+ else if (known.isEmpty())
+ parseType(inner["!type"], innerPath, null, true).propagate(known);
+ else
+ continue;
+ if (inner["!doc"]) known.doc = inner["!doc"];
+ if (inner["!url"]) known.url = inner["!url"];
+ if (inner["!span"]) known.span = inner["!span"];
+ }
+ }
+ return base;
+ }
+
+ function copyInfo(spec, type) {
+ if (spec["!doc"]) type.doc = spec["!doc"];
+ if (spec["!url"]) type.url = spec["!url"];
+ if (spec["!span"]) type.span = spec["!span"];
+ if (spec["!data"]) type.metaData = spec["!data"];
+ }
+
+ function doLoadEnvironment(data, scope) {
+ var cx = infer.cx(), server = cx.parent
+
+ infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length);
+ cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null);
+
+ if (server) server.signal("preLoadDef", data)
+
+ passOne(scope, data);
+
+ var def = data["!define"];
+ if (def) {
+ for (var name in def) {
+ var spec = def[name];
+ cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name);
+ }
+ for (var name in def) {
+ var spec = def[name];
+ if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name);
+ }
+ }
+
+ passTwo(scope, data);
+
+ if (server) server.signal("postLoadDef", data)
+
+ cx.curOrigin = cx.localDefs = null;
+ }
+
+ exports.load = function(data, scope) {
+ if (!scope) scope = infer.cx().topScope;
+ var oldScope = currentTopScope;
+ currentTopScope = scope;
+ try {
+ doLoadEnvironment(data, scope);
+ } finally {
+ currentTopScope = oldScope;
+ }
+ };
+
+ exports.parse = function(data, origin, path) {
+ var cx = infer.cx();
+ if (origin) {
+ cx.origin = origin;
+ cx.localDefs = cx.definitions[origin];
+ }
+
+ try {
+ if (typeof data == "string")
+ return parseType(data, path);
+ else
+ return passTwo(passOne(null, data, path), data, path);
+ } finally {
+ if (origin) cx.origin = cx.localDefs = null;
+ }
+ };
+
+ // Used to register custom logic for more involved effect or type
+ // computation.
+ var customFunctions = Object.create(null);
+ infer.registerFunction = function(name, f) { customFunctions[name] = f; };
+
+ var IsCreated = infer.constraint({
+ construct: function(created, target, spec) {
+ this.created = created;
+ this.target = target;
+ this.spec = spec;
+ },
+ addType: function(tp) {
+ if (tp instanceof infer.Obj && this.created++ < 5) {
+ var derived = new infer.Obj(tp), spec = this.spec;
+ if (spec instanceof infer.AVal) spec = spec.getObjType(false);
+ if (spec instanceof infer.Obj) for (var prop in spec.props) {
+ var cur = spec.props[prop].types[0];
+ var p = derived.defProp(prop);
+ if (cur && cur instanceof infer.Obj && cur.props.value) {
+ var vtp = cur.props.value.getType(false);
+ if (vtp) p.addType(vtp);
+ }
+ }
+ this.target.addType(derived);
+ }
+ }
+ });
+
+ infer.registerFunction("Object_create", function(_self, args, argNodes) {
+ if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null)
+ return new infer.Obj();
+
+ var result = new infer.AVal;
+ if (args[0]) args[0].propagate(new IsCreated(0, result, args[1]));
+ return result;
+ });
+
+ var PropSpec = infer.constraint({
+ construct: function(target) { this.target = target; },
+ addType: function(tp) {
+ if (!(tp instanceof infer.Obj)) return;
+ if (tp.hasProp("value"))
+ tp.getProp("value").propagate(this.target);
+ else if (tp.hasProp("get"))
+ tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
+ }
+ });
+
+ infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
+ if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
+ typeof argNodes[1].value == "string") {
+ var obj = args[0], connect = new infer.AVal;
+ obj.propagate(new infer.DefProp(argNodes[1].value, connect, argNodes[1]));
+ args[2].propagate(new PropSpec(connect));
+ }
+ return infer.ANull;
+ });
+
+ infer.registerFunction("Object_defineProperties", function(_self, args, argNodes) {
+ if (args.length >= 2) {
+ var obj = args[0];
+ args[1].forAllProps(function(prop, val, local) {
+ if (!local) return;
+ var connect = new infer.AVal;
+ obj.propagate(new infer.DefProp(prop, connect, argNodes && argNodes[1]));
+ val.propagate(new PropSpec(connect));
+ });
+ }
+ return infer.ANull;
+ });
+
+ var IsBound = infer.constraint({
+ construct: function(self, args, target) {
+ this.self = self; this.args = args; this.target = target;
+ },
+ addType: function(tp) {
+ if (!(tp instanceof infer.Fn)) return;
+ this.target.addType(new infer.Fn(tp.name, infer.ANull, tp.args.slice(this.args.length),
+ tp.argNames.slice(this.args.length), tp.retval, tp.generator));
+ this.self.propagate(tp.self);
+ for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
+ this.args[i].propagate(tp.args[i]);
+ }
+ });
+
+ infer.registerFunction("Function_bind", function(self, args) {
+ if (!args.length) return infer.ANull;
+ var result = new infer.AVal;
+ self.propagate(new IsBound(args[0], args.slice(1), result));
+ return result;
+ });
+
+ infer.registerFunction("Array_ctor", function(_self, args) {
+ var arr = new infer.Arr;
+ if (args.length != 1 || !args[0].hasType(infer.cx().num)) {
+ var content = arr.getProp("<i>");
+ for (var i = 0; i < args.length; ++i) args[i].propagate(content);
+ }
+ return arr;
+ });
+
+ infer.registerFunction("Promise_ctor", function(_self, args, argNodes) {
+ var defs6 = infer.cx().definitions.ecma6
+ if (!defs6 || args.length < 1) return infer.ANull;
+ var self = new infer.Obj(defs6["Promise.prototype"]);
+ var valProp = self.defProp(":t", argNodes && argNodes[0]);
+ var valArg = new infer.AVal;
+ valArg.propagate(valProp);
+ var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull);
+ var reject = defs6.Promise_reject;
+ args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull));
+ return self;
+ });
+
+ var PromiseResolvesTo = infer.constraint({
+ construct: function(output) { this.output = output; },
+ addType: function(tp) {
+ if (tp.constructor == infer.Obj && tp.name == "Promise" && tp.hasProp(":t"))
+ tp.getProp(":t").propagate(this.output);
+ else
+ tp.propagate(this.output);
+ }
+ });
+
+ var WG_PROMISE_KEEP_VALUE = 50;
+
+ infer.registerFunction("Promise_then", function(self, args, argNodes) {
+ var fn = args.length && args[0].getFunctionType();
+ var defs6 = infer.cx().definitions.ecma6
+ if (!fn || !defs6) return self;
+
+ var result = new infer.Obj(defs6["Promise.prototype"]);
+ var value = result.defProp(":t", argNodes && argNodes[0]), ty;
+ if (fn.retval.isEmpty() && (ty = self.getType()) instanceof infer.Obj && ty.hasProp(":t"))
+ ty.getProp(":t").propagate(value, WG_PROMISE_KEEP_VALUE);
+ fn.retval.propagate(new PromiseResolvesTo(value));
+ return result;
+ });
+
+ infer.registerFunction("getOwnPropertySymbols", function(_self, args) {
+ if (!args.length) return infer.ANull
+ var result = new infer.AVal
+ args[0].forAllProps(function(prop, _val, local) {
+ if (local && prop.charAt(0) == ":") result.addType(infer.getSymbol(prop.slice(1)))
+ })
+ return result
+ })
+
+ infer.registerFunction("getSymbol", function(_self, _args, argNodes) {
+ if (argNodes.length && argNodes[0].type == "Literal" && typeof argNodes[0].value == "string")
+ return infer.getSymbol(argNodes[0].value)
+ else
+ return infer.ANull
+ })
+
+ return exports;
+});