function.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /**
  2. * @namespace Sk.builtin
  3. */
  4. /**
  5. * Check arguments to Python functions to ensure the correct number of
  6. * arguments are passed.
  7. *
  8. * @param {string} name the name of the function
  9. * @param {Object} args the args passed to the function
  10. * @param {number} minargs the minimum number of allowable arguments
  11. * @param {number=} maxargs optional maximum number of allowable
  12. * arguments (default: Infinity)
  13. * @param {boolean=} kwargs optional true if kwargs, false otherwise
  14. * (default: false)
  15. * @param {boolean=} free optional true if free vars, false otherwise
  16. * (default: false)
  17. */
  18. Sk.builtin.pyCheckArgs = function (name, args, minargs, maxargs, kwargs, free) {
  19. var nargs = args.length;
  20. var msg = "";
  21. if (maxargs === undefined) {
  22. maxargs = Infinity;
  23. }
  24. if (kwargs) {
  25. nargs -= 1;
  26. }
  27. if (free) {
  28. nargs -= 1;
  29. }
  30. if ((nargs < minargs) || (nargs > maxargs)) {
  31. if (minargs === maxargs) {
  32. msg = name + "() takes exactly " + minargs + " arguments";
  33. } else if (nargs < minargs) {
  34. msg = name + "() takes at least " + minargs + " arguments";
  35. } else {
  36. msg = name + "() takes at most " + maxargs + " arguments";
  37. }
  38. msg += " (" + nargs + " given)";
  39. throw new Sk.builtin.TypeError(msg);
  40. }
  41. };
  42. goog.exportSymbol("Sk.builtin.pyCheckArgs", Sk.builtin.pyCheckArgs);
  43. /**
  44. * Check type of argument to Python functions.
  45. *
  46. * @param {string} name the name of the argument
  47. * @param {string} exptype string of the expected type name
  48. * @param {boolean} check truthy if type check passes, falsy otherwise
  49. */
  50. Sk.builtin.pyCheckType = function (name, exptype, check) {
  51. if (!check) {
  52. throw new Sk.builtin.TypeError(name + " must be a " + exptype);
  53. }
  54. };
  55. goog.exportSymbol("Sk.builtin.pyCheckType", Sk.builtin.pyCheckType);
  56. Sk.builtin.checkSequence = function (arg) {
  57. return (arg !== null && arg.mp$subscript !== undefined);
  58. };
  59. goog.exportSymbol("Sk.builtin.checkSequence", Sk.builtin.checkSequence);
  60. /**
  61. * Use this to test whether or not a Python object is iterable. You should **not** rely
  62. * on the presence of tp$iter on the object as a good test, as it could be a user defined
  63. * class with `__iter__` defined or ``__getitem__`` This tests for all of those cases
  64. *
  65. * @param arg {Object} A Python object
  66. * @returns {boolean} true if the object is iterable
  67. */
  68. Sk.builtin.checkIterable = function (arg) {
  69. var ret = false;
  70. if (arg !== null ) {
  71. try {
  72. ret = Sk.abstr.iter(arg);
  73. if (ret) {
  74. return true;
  75. } else {
  76. return false;
  77. }
  78. } catch (e) {
  79. if (e instanceof Sk.builtin.TypeError) {
  80. return false;
  81. } else {
  82. throw e;
  83. }
  84. }
  85. }
  86. return ret;
  87. };
  88. goog.exportSymbol("Sk.builtin.checkIterable", Sk.builtin.checkIterable);
  89. Sk.builtin.checkCallable = function (obj) {
  90. // takes care of builtin functions and methods, builtins
  91. if (typeof obj === "function") {
  92. return true;
  93. }
  94. // takes care of python function, methods and lambdas
  95. if (obj instanceof Sk.builtin.func) {
  96. return true;
  97. }
  98. // takes care of instances of methods
  99. if (obj instanceof Sk.builtin.method) {
  100. return true;
  101. }
  102. // go up the prototype chain to see if the class has a __call__ method
  103. if (Sk.abstr.lookupSpecial(obj, "__call__") !== undefined) {
  104. return true;
  105. }
  106. return false;
  107. };
  108. Sk.builtin.checkNumber = function (arg) {
  109. return (arg !== null && (typeof arg === "number" ||
  110. arg instanceof Sk.builtin.int_ ||
  111. arg instanceof Sk.builtin.float_ ||
  112. arg instanceof Sk.builtin.lng));
  113. };
  114. goog.exportSymbol("Sk.builtin.checkNumber", Sk.builtin.checkNumber);
  115. /**
  116. * Checks for complex type, delegates to internal method
  117. * Most skulpt users would search here!
  118. */
  119. Sk.builtin.checkComplex = function (arg) {
  120. return Sk.builtin.complex._complex_check(arg);
  121. };
  122. goog.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex);
  123. Sk.builtin.checkInt = function (arg) {
  124. return (arg !== null) && ((typeof arg === "number" && arg === (arg | 0)) ||
  125. arg instanceof Sk.builtin.int_ ||
  126. arg instanceof Sk.builtin.lng);
  127. };
  128. goog.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt);
  129. Sk.builtin.checkFloat = function (arg) {
  130. return (arg !== null) && (arg instanceof Sk.builtin.float_);
  131. };
  132. goog.exportSymbol("Sk.builtin.checkFloat", Sk.builtin.checkFloat);
  133. Sk.builtin.checkString = function (arg) {
  134. return (arg !== null && arg.__class__ == Sk.builtin.str);
  135. };
  136. goog.exportSymbol("Sk.builtin.checkString", Sk.builtin.checkString);
  137. Sk.builtin.checkClass = function (arg) {
  138. return (arg !== null && arg.sk$type);
  139. };
  140. goog.exportSymbol("Sk.builtin.checkClass", Sk.builtin.checkClass);
  141. Sk.builtin.checkBool = function (arg) {
  142. return (arg instanceof Sk.builtin.bool);
  143. };
  144. goog.exportSymbol("Sk.builtin.checkBool", Sk.builtin.checkBool);
  145. Sk.builtin.checkNone = function (arg) {
  146. return (arg instanceof Sk.builtin.none);
  147. };
  148. goog.exportSymbol("Sk.builtin.checkNone", Sk.builtin.checkNone);
  149. Sk.builtin.checkFunction = function (arg) {
  150. return (arg !== null && arg.tp$call !== undefined);
  151. };
  152. goog.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction);
  153. /**
  154. * @constructor
  155. * Sk.builtin.func
  156. *
  157. * @description
  158. * This function converts a Javascript function into a Python object that is callable. Or just
  159. * think of it as a Python function rather than a Javascript function now. This is an important
  160. * distinction in skulpt because once you have Python function you cannot just call it.
  161. * You must now use Sk.misceval.callsim to call the Python function.
  162. *
  163. * @param {Function} code the javascript implementation of this function
  164. * @param {Object=} globals the globals where this function was defined.
  165. * Can be undefined (which will be stored as null) for builtins. (is
  166. * that ok?)
  167. * @param {Object=} closure dict of free variables
  168. * @param {Object=} closure2 another dict of free variables that will be
  169. * merged into 'closure'. there's 2 to simplify generated code (one is $free,
  170. * the other is $cell)
  171. *
  172. * closure is the cell variables from the parent scope that we need to close
  173. * over. closure2 is the free variables in the parent scope that we also might
  174. * need to access.
  175. *
  176. * NOTE: co_varnames and co_name are defined by compiled code only, so we have
  177. * to access them via dict-style lookup for closure.
  178. *
  179. */
  180. Sk.builtin.func = function (code, globals, closure, closure2) {
  181. var k;
  182. this.func_code = code;
  183. this.func_globals = globals || null;
  184. if (closure2 !== undefined) {
  185. // todo; confirm that modification here can't cause problems
  186. for (k in closure2) {
  187. closure[k] = closure2[k];
  188. }
  189. }
  190. this.func_closure = closure;
  191. return this;
  192. };
  193. goog.exportSymbol("Sk.builtin.func", Sk.builtin.func);
  194. Sk.builtin.func.prototype.tp$name = "function";
  195. Sk.builtin.func.prototype.tp$descr_get = function (obj, objtype) {
  196. goog.asserts.assert(obj !== undefined && objtype !== undefined);
  197. if (obj === Sk.builtin.none.none$) {
  198. return this;
  199. }
  200. return new Sk.builtin.method(this, obj, objtype);
  201. };
  202. Sk.builtin.func.prototype.tp$call = function (args, kw) {
  203. var j;
  204. var i;
  205. var numvarnames;
  206. var varnames;
  207. var kwlen;
  208. var kwargsarr;
  209. var expectskw;
  210. var name;
  211. var numargs;
  212. expectskw = this.func_code["co_kwargs"];
  213. kwargsarr = [];
  214. if (this.func_code["no_kw"] && kw) {
  215. name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
  216. throw new Sk.builtin.TypeError(name + "() takes no keyword arguments");
  217. }
  218. if (kw) {
  219. // bind the kw args
  220. kwlen = kw.length;
  221. varnames = this.func_code["co_varnames"];
  222. numvarnames = varnames && varnames.length;
  223. for (i = 0; i < kwlen; i += 2) {
  224. // todo; make this a dict mapping name to offset
  225. for (j = 0; j < numvarnames; ++j) {
  226. if (kw[i] === varnames[j]) {
  227. break;
  228. }
  229. }
  230. if (varnames && j !== numvarnames) {
  231. if (j in args) {
  232. name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
  233. throw new Sk.builtin.TypeError(name + "() got multiple values for keyword argument '" + kw[i] + "'");
  234. }
  235. args[j] = kw[i + 1];
  236. } else if (expectskw) {
  237. // build kwargs dict
  238. kwargsarr.push(new Sk.builtin.str(kw[i]));
  239. kwargsarr.push(kw[i + 1]);
  240. } else {
  241. name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
  242. throw new Sk.builtin.TypeError(name + "() got an unexpected keyword argument '" + kw[i] + "'");
  243. }
  244. }
  245. }
  246. if (this.func_closure) {
  247. // todo; OK to modify?
  248. if (this.func_code["co_varnames"]) {
  249. // Make sure all default arguments are in args before adding closure
  250. numargs = args.length;
  251. numvarnames = this.func_code["co_varnames"].length;
  252. for (i = numargs; i < numvarnames; i++) {
  253. args.push(undefined);
  254. }
  255. }
  256. args.push(this.func_closure);
  257. }
  258. if (expectskw) {
  259. args.unshift(kwargsarr);
  260. }
  261. //print(JSON.stringify(args, null, 2));
  262. // note: functions expect 'this' to be globals to avoid having to
  263. // slice/unshift onto the main args
  264. return this.func_code.apply(this.func_globals, args);
  265. };
  266. Sk.builtin.func.prototype.tp$getattr = function (key) {
  267. return this[key];
  268. };
  269. Sk.builtin.func.prototype.tp$setattr = function (key, value) {
  270. this[key] = value;
  271. };
  272. //todo; investigate why the other doesn't work
  273. //Sk.builtin.type.makeIntoTypeObj('function', Sk.builtin.func);
  274. Sk.builtin.func.prototype.ob$type = Sk.builtin.type.makeTypeObj("function", new Sk.builtin.func(null, null));
  275. Sk.builtin.func.prototype["$r"] = function () {
  276. var name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
  277. return new Sk.builtin.str("<function " + name + ">");
  278. };