jsffi.nim 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2017 Nim Authors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This Module implements types and macros to facilitate the wrapping of, and
  10. ## interaction with JavaScript libraries. Using the provided types `JsObject`
  11. ## and `JsAssoc` together with the provided macros allows for smoother
  12. ## interfacing with JavaScript, allowing for example quick and easy imports of
  13. ## JavaScript variables:
  14. runnableExamples:
  15. # Here, we are using jQuery for just a few calls and do not want to wrap the
  16. # whole library:
  17. # import the document object and the console
  18. var document {.importc, nodecl.}: JsObject
  19. var console {.importc, nodecl.}: JsObject
  20. # import the "$" function
  21. proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".}
  22. # Use jQuery to make the following code run, after the document is ready.
  23. # This uses an experimental `.()` operator for `JsObject`, to emit
  24. # JavaScript calls, when no corresponding proc exists for `JsObject`.
  25. proc main =
  26. jq(document).ready(proc() =
  27. console.log("Hello JavaScript!")
  28. )
  29. when not defined(js) and not defined(nimsuggest):
  30. {.fatal: "Module jsFFI is designed to be used with the JavaScript backend.".}
  31. import std/[macros, tables]
  32. const
  33. setImpl = "#[#] = #"
  34. getImpl = "#[#]"
  35. var
  36. mangledNames {.compileTime.} = initTable[string, string]()
  37. nameCounter {.compileTime.} = 0
  38. proc validJsName(name: string): bool =
  39. result = true
  40. const reservedWords = ["break", "case", "catch", "class", "const", "continue",
  41. "debugger", "default", "delete", "do", "else", "export", "extends",
  42. "finally", "for", "function", "if", "import", "in", "instanceof", "new",
  43. "return", "super", "switch", "this", "throw", "try", "typeof", "var",
  44. "void", "while", "with", "yield", "enum", "implements", "interface",
  45. "let", "package", "private", "protected", "public", "static", "await",
  46. "abstract", "boolean", "byte", "char", "double", "final", "float", "goto",
  47. "int", "long", "native", "short", "synchronized", "throws", "transient",
  48. "volatile", "null", "true", "false"]
  49. case name
  50. of reservedWords: return false
  51. else: discard
  52. if name[0] notin {'A'..'Z','a'..'z','_','$'}: return false
  53. for chr in name:
  54. if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
  55. return false
  56. template mangleJsName(name: string): string =
  57. inc nameCounter
  58. "mangledName" & $nameCounter
  59. # only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring
  60. proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importjs: "parseInt(#)".}
  61. proc toJsKey*[T: enum](text: cstring, t: type T): T =
  62. T(text.toJsKey(int))
  63. proc toJsKey*(text: cstring, t: type cstring): cstring =
  64. text
  65. proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importjs: "parseFloat(#)".}
  66. type
  67. JsKey* = concept a, type T
  68. cstring.toJsKey(T) is T
  69. JsObject* = ref object of JsRoot
  70. ## Dynamically typed wrapper around a JavaScript object.
  71. JsAssoc*[K: JsKey, V] = ref object of JsRoot
  72. ## Statically typed wrapper around a JavaScript object.
  73. js* = JsObject
  74. var
  75. jsArguments* {.importc: "arguments", nodecl}: JsObject
  76. ## JavaScript's arguments pseudo-variable.
  77. jsNull* {.importc: "null", nodecl.}: JsObject
  78. ## JavaScript's null literal.
  79. jsUndefined* {.importc: "undefined", nodecl.}: JsObject
  80. ## JavaScript's undefined literal.
  81. jsDirname* {.importc: "__dirname", nodecl.}: cstring
  82. ## JavaScript's __dirname pseudo-variable.
  83. jsFilename* {.importc: "__filename", nodecl.}: cstring
  84. ## JavaScript's __filename pseudo-variable.
  85. proc isNull*[T](x: T): bool {.noSideEffect, importjs: "(# === null)".}
  86. ## Checks if a value is exactly null.
  87. proc isUndefined*[T](x: T): bool {.noSideEffect, importjs: "(# === undefined)".}
  88. ## Checks if a value is exactly undefined.
  89. # Exceptions
  90. type
  91. JsError* {.importc: "Error".} = object of JsRoot
  92. message*: cstring
  93. JsEvalError* {.importc: "EvalError".} = object of JsError
  94. JsRangeError* {.importc: "RangeError".} = object of JsError
  95. JsReferenceError* {.importc: "ReferenceError".} = object of JsError
  96. JsSyntaxError* {.importc: "SyntaxError".} = object of JsError
  97. JsTypeError* {.importc: "TypeError".} = object of JsError
  98. JsURIError* {.importc: "URIError".} = object of JsError
  99. # New
  100. proc newJsObject*: JsObject {.importjs: "{@}".}
  101. ## Creates a new empty JsObject.
  102. proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importjs: "{@}".}
  103. ## Creates a new empty JsAssoc with key type `K` and value type `V`.
  104. # Checks
  105. proc hasOwnProperty*(x: JsObject, prop: cstring): bool
  106. {.importjs: "#.hasOwnProperty(#)".}
  107. ## Checks, whether `x` has a property of name `prop`.
  108. proc jsTypeOf*(x: JsObject): cstring {.importjs: "typeof(#)".}
  109. ## Returns the name of the JsObject's JavaScript type as a cstring.
  110. proc jsNew*(x: auto): JsObject {.importjs: "(new #)".}
  111. ## Turns a regular function call into an invocation of the
  112. ## JavaScript's `new` operator.
  113. proc jsDelete*(x: auto): JsObject {.importjs: "(delete #)".}
  114. ## JavaScript's `delete` operator.
  115. proc require*(module: cstring): JsObject {.importc.}
  116. ## JavaScript's `require` function.
  117. # Conversion to and from JsObject
  118. proc to*(x: JsObject, T: typedesc): T {.importjs: "(#)".}
  119. ## Converts a JsObject `x` to type `T`.
  120. proc toJs*[T](val: T): JsObject {.importjs: "(#)".}
  121. ## Converts a value of any type to type JsObject.
  122. template toJs*(s: string): JsObject = cstring(s).toJs
  123. macro jsFromAst*(n: untyped): untyped =
  124. result = n
  125. if n.kind == nnkStmtList:
  126. result = newProc(procType = nnkDo, body = result)
  127. return quote: toJs(`result`)
  128. proc `&`*(a, b: cstring): cstring {.importjs: "(# + #)".}
  129. ## Concatenation operator for JavaScript strings.
  130. proc `+`*(x, y: JsObject): JsObject {.importjs: "(# + #)".}
  131. proc `-`*(x, y: JsObject): JsObject {.importjs: "(# - #)".}
  132. proc `*`*(x, y: JsObject): JsObject {.importjs: "(# * #)".}
  133. proc `/`*(x, y: JsObject): JsObject {.importjs: "(# / #)".}
  134. proc `%`*(x, y: JsObject): JsObject {.importjs: "(# % #)".}
  135. proc `+=`*(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.}
  136. proc `-=`*(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.}
  137. proc `*=`*(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.}
  138. proc `/=`*(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.}
  139. proc `%=`*(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.}
  140. proc `++`*(x: JsObject): JsObject {.importjs: "(++#)".}
  141. proc `--`*(x: JsObject): JsObject {.importjs: "(--#)".}
  142. proc `>`*(x, y: JsObject): JsObject {.importjs: "(# > #)".}
  143. proc `<`*(x, y: JsObject): JsObject {.importjs: "(# < #)".}
  144. proc `>=`*(x, y: JsObject): JsObject {.importjs: "(# >= #)".}
  145. proc `<=`*(x, y: JsObject): JsObject {.importjs: "(# <= #)".}
  146. proc `**`*(x, y: JsObject): JsObject {.importjs: "((#) ** #)".}
  147. # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812
  148. proc `and`*(x, y: JsObject): JsObject {.importjs: "(# && #)".}
  149. proc `or`*(x, y: JsObject): JsObject {.importjs: "(# || #)".}
  150. proc `not`*(x: JsObject): JsObject {.importjs: "(!#)".}
  151. proc `in`*(x, y: JsObject): JsObject {.importjs: "(# in #)".}
  152. proc `[]`*(obj: JsObject, field: cstring): JsObject {.importjs: getImpl.}
  153. ## Returns the value of a property of name `field` from a JsObject `obj`.
  154. proc `[]`*(obj: JsObject, field: int): JsObject {.importjs: getImpl.}
  155. ## Returns the value of a property of name `field` from a JsObject `obj`.
  156. proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importjs: setImpl.}
  157. ## Sets the value of a property of name `field` in a JsObject `obj` to `v`.
  158. proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importjs: setImpl.}
  159. ## Sets the value of a property of name `field` in a JsObject `obj` to `v`.
  160. proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V
  161. {.importjs: getImpl.}
  162. ## Returns the value of a property of name `field` from a JsAssoc `obj`.
  163. proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V)
  164. {.importjs: setImpl.}
  165. ## Sets the value of a property of name `field` in a JsAssoc `obj` to `v`.
  166. proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V =
  167. obj[cstring(field)]
  168. proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) =
  169. obj[cstring(field)] = val
  170. proc `==`*(x, y: JsRoot): bool {.importjs: "(# === #)".}
  171. ## Compares two JsObjects or JsAssocs. Be careful though, as this is comparison
  172. ## like in JavaScript, so if your JsObjects are in fact JavaScript Objects,
  173. ## and not strings or numbers, this is a *comparison of references*.
  174. {.experimental.}
  175. macro `.`*(obj: JsObject, field: untyped): JsObject =
  176. ## Experimental dot accessor (get) for type JsObject.
  177. ## Returns the value of a property of name `field` from a JsObject `x`.
  178. runnableExamples:
  179. let obj = newJsObject()
  180. obj.a = 20
  181. assert obj.a.to(int) == 20
  182. if validJsName($field):
  183. let importString = "#." & $field
  184. let helperName = genSym(nskProc, "helper")
  185. result = quote do:
  186. proc `helperName`(o: JsObject): JsObject
  187. {.importjs: `importString`.}
  188. `helperName`(`obj`)
  189. else:
  190. if not mangledNames.hasKey($field):
  191. mangledNames[$field] = mangleJsName($field)
  192. let importString = "#." & mangledNames[$field]
  193. let helperName = genSym(nskProc, "helper")
  194. result = quote do:
  195. proc `helperName`(o: JsObject): JsObject
  196. {.importjs: `importString`.}
  197. `helperName`(`obj`)
  198. macro `.=`*(obj: JsObject, field, value: untyped): untyped =
  199. ## Experimental dot accessor (set) for type JsObject.
  200. ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
  201. if validJsName($field):
  202. let importString = "#." & $field & " = #"
  203. let helperName = genSym(nskProc, "helper")
  204. result = quote do:
  205. proc `helperName`(o: JsObject, v: auto)
  206. {.importjs: `importString`.}
  207. `helperName`(`obj`, `value`)
  208. else:
  209. if not mangledNames.hasKey($field):
  210. mangledNames[$field] = mangleJsName($field)
  211. let importString = "#." & mangledNames[$field] & " = #"
  212. let helperName = genSym(nskProc, "helper")
  213. result = quote do:
  214. proc `helperName`(o: JsObject, v: auto)
  215. {.importjs: `importString`.}
  216. `helperName`(`obj`, `value`)
  217. macro `.()`*(obj: JsObject,
  218. field: untyped,
  219. args: varargs[JsObject, jsFromAst]): JsObject =
  220. ## Experimental "method call" operator for type JsObject.
  221. ## Takes the name of a method of the JavaScript object (`field`) and calls
  222. ## it with `args` as arguments, returning a JsObject (which may be discarded,
  223. ## and may be `undefined`, if the method does not return anything,
  224. ## so be careful when using this.)
  225. ##
  226. ## Example:
  227. ## ```nim
  228. ## # Let's get back to the console example:
  229. ## var console {.importc, nodecl.}: JsObject
  230. ## let res = console.log("I return undefined!")
  231. ## console.log(res) # This prints undefined, as console.log always returns
  232. ## # undefined. Thus one has to be careful, when using
  233. ## # JsObject calls.
  234. ## ```
  235. var importString: string
  236. if validJsName($field):
  237. importString = "#." & $field & "(@)"
  238. else:
  239. if not mangledNames.hasKey($field):
  240. mangledNames[$field] = mangleJsName($field)
  241. importString = "#." & mangledNames[$field] & "(@)"
  242. let helperName = genSym(nskProc, "helper")
  243. result = quote do:
  244. proc `helperName`(o: JsObject): JsObject
  245. {.importjs: `importString`, discardable.}
  246. `helperName`(`obj`)
  247. for idx in 0 ..< args.len:
  248. let paramName = newIdentNode("param" & $idx)
  249. result[0][3].add newIdentDefs(paramName, newIdentNode("JsObject"))
  250. result[1].add args[idx].copyNimTree
  251. macro `.`*[K: cstring, V](obj: JsAssoc[K, V],
  252. field: untyped): V =
  253. ## Experimental dot accessor (get) for type JsAssoc.
  254. ## Returns the value of a property of name `field` from a JsObject `x`.
  255. var importString: string
  256. if validJsName($field):
  257. importString = "#." & $field
  258. else:
  259. if not mangledNames.hasKey($field):
  260. mangledNames[$field] = mangleJsName($field)
  261. importString = "#." & mangledNames[$field]
  262. let helperName = genSym(nskProc, "helper")
  263. result = quote do:
  264. proc `helperName`(o: type(`obj`)): `obj`.V
  265. {.importjs: `importString`.}
  266. `helperName`(`obj`)
  267. macro `.=`*[K: cstring, V](obj: JsAssoc[K, V],
  268. field: untyped,
  269. value: V): untyped =
  270. ## Experimental dot accessor (set) for type JsAssoc.
  271. ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
  272. var importString: string
  273. if validJsName($field):
  274. importString = "#." & $field & " = #"
  275. else:
  276. if not mangledNames.hasKey($field):
  277. mangledNames[$field] = mangleJsName($field)
  278. importString = "#." & mangledNames[$field] & " = #"
  279. let helperName = genSym(nskProc, "helper")
  280. result = quote do:
  281. proc `helperName`(o: type(`obj`), v: `obj`.V)
  282. {.importjs: `importString`.}
  283. `helperName`(`obj`, `value`)
  284. macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V],
  285. field: untyped,
  286. args: varargs[untyped]): auto =
  287. ## Experimental "method call" operator for type JsAssoc.
  288. ## Takes the name of a method of the JavaScript object (`field`) and calls
  289. ## it with `args` as arguments. Here, everything is typechecked, so you do not
  290. ## have to worry about `undefined` return values.
  291. let dotOp = bindSym"."
  292. result = quote do:
  293. (`dotOp`(`obj`, `field`))()
  294. for elem in args:
  295. result.add elem
  296. # Iterators:
  297. iterator pairs*(obj: JsObject): (cstring, JsObject) =
  298. ## Yields tuples of type `(cstring, JsObject)`, with the first entry
  299. ## being the `name` of a fields in the JsObject and the second being its
  300. ## value wrapped into a JsObject.
  301. var k: cstring
  302. var v: JsObject
  303. {.emit: "for (var `k` in `obj`) {".}
  304. {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".}
  305. {.emit: " `v` = `obj`[`k`];".}
  306. yield (k, v)
  307. {.emit: "}".}
  308. iterator items*(obj: JsObject): JsObject =
  309. ## Yields the `values` of each field in a JsObject, wrapped into a JsObject.
  310. var v: JsObject
  311. {.emit: "for (var k in `obj`) {".}
  312. {.emit: " if (!`obj`.hasOwnProperty(k)) { continue; }".}
  313. {.emit: " `v` = `obj`[k];".}
  314. yield v
  315. {.emit: "}".}
  316. iterator keys*(obj: JsObject): cstring =
  317. ## Yields the `names` of each field in a JsObject.
  318. var k: cstring
  319. {.emit: "for (var `k` in `obj`) {".}
  320. {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".}
  321. yield k
  322. {.emit: "}".}
  323. iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) =
  324. ## Yields tuples of type `(K, V)`, with the first entry
  325. ## being a `key` in the JsAssoc and the second being its corresponding value.
  326. var k: cstring
  327. var v: V
  328. {.emit: "for (var `k` in `assoc`) {".}
  329. {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".}
  330. {.emit: " `v` = `assoc`[`k`];".}
  331. yield (k.toJsKey(K), v)
  332. {.emit: "}".}
  333. iterator items*[K, V](assoc: JsAssoc[K, V]): V =
  334. ## Yields the `values` in a JsAssoc.
  335. var v: V
  336. {.emit: "for (var k in `assoc`) {".}
  337. {.emit: " if (!`assoc`.hasOwnProperty(k)) { continue; }".}
  338. {.emit: " `v` = `assoc`[k];".}
  339. yield v
  340. {.emit: "}".}
  341. iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K =
  342. ## Yields the `keys` in a JsAssoc.
  343. var k: cstring
  344. {.emit: "for (var `k` in `assoc`) {".}
  345. {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".}
  346. yield k.toJsKey(K)
  347. {.emit: "}".}
  348. # Literal generation
  349. macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
  350. ## Takes a `typedesc` as its first argument, and a series of expressions of
  351. ## type `key: value`, and returns a value of the specified type with each
  352. ## field `key` set to `value`, as specified in the arguments of `{}`.
  353. ##
  354. ## Example:
  355. ##
  356. ## ```nim
  357. ## # Let's say we have a type with a ton of fields, where some fields do not
  358. ## # need to be set, and we do not want those fields to be set to `nil`:
  359. ## type
  360. ## ExtremelyHugeType = ref object
  361. ## a, b, c, d, e, f, g: int
  362. ## h, i, j, k, l: cstring
  363. ## # And even more fields ...
  364. ##
  365. ## let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }
  366. ##
  367. ## # This generates roughly the same JavaScript as:
  368. ## {.emit: "var obj = {a: 1, k: "foo", d: 42};".}
  369. ## ```
  370. let a = ident"a"
  371. var body = quote do:
  372. var `a` {.noinit.}: `typ`
  373. {.emit: "`a` = {};".}
  374. for x in xs.children:
  375. if x.kind == nnkExprColonExpr:
  376. let
  377. k = x[0]
  378. kString = quote do:
  379. when compiles($`k`): $`k` else: "invalid"
  380. v = x[1]
  381. body.add quote do:
  382. when compiles(`a`.`k`):
  383. `a`.`k` = `v`
  384. elif compiles(`a`[`k`]):
  385. `a`[`k`] = `v`
  386. else:
  387. `a`[`kString`] = `v`
  388. else:
  389. error("Expression `" & $x.toStrLit & "` not allowed in `{}` macro")
  390. body.add quote do:
  391. return `a`
  392. result = quote do:
  393. proc inner(): `typ` {.gensym.} =
  394. `body`
  395. inner()
  396. # Macro to build a lambda using JavaScript's `this`
  397. # from a proc, `this` being the first argument.
  398. proc replaceSyms(n: NimNode): NimNode =
  399. if n.kind == nnkSym:
  400. result = newIdentNode($n)
  401. else:
  402. result = n
  403. for i in 0..<n.len:
  404. result[i] = replaceSyms(n[i])
  405. macro bindMethod*(procedure: typed): auto {.deprecated: "Don't use it with closures".} =
  406. ## Takes the name of a procedure and wraps it into a lambda missing the first
  407. ## argument, which passes the JavaScript builtin `this` as the first
  408. ## argument to the procedure. Returns the resulting lambda.
  409. ##
  410. ## Example:
  411. ##
  412. ## We want to generate roughly this JavaScript:
  413. ## ```js
  414. ## var obj = {a: 10};
  415. ## obj.someMethod = function() {
  416. ## return this.a + 42;
  417. ## };
  418. ## ```
  419. ##
  420. ## We can achieve this using the `bindMethod` macro:
  421. ##
  422. ## ```nim
  423. ## let obj = JsObject{ a: 10 }
  424. ## proc someMethodImpl(that: JsObject): int =
  425. ## that.a.to(int) + 42
  426. ## obj.someMethod = bindMethod someMethodImpl
  427. ##
  428. ## # Alternatively:
  429. ## obj.someMethod = bindMethod
  430. ## proc(that: JsObject): int = that.a.to(int) + 42
  431. ## ```
  432. if not (procedure.kind == nnkSym or procedure.kind == nnkLambda):
  433. error("Argument has to be a proc or a symbol corresponding to a proc.")
  434. var
  435. rawProc = if procedure.kind == nnkSym:
  436. getImpl(procedure)
  437. else:
  438. procedure
  439. args = rawProc[3].copyNimTree.replaceSyms
  440. thisType = args[1][1]
  441. params = newNimNode(nnkFormalParams).add(args[0])
  442. body = newNimNode(nnkLambda)
  443. this = newIdentNode("this")
  444. # construct the `this` parameter:
  445. thisQuote = quote do:
  446. var `this` {.nodecl, importc: "this".}: `thisType`
  447. call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0])
  448. # construct the procedure call inside the method
  449. if args.len > 2:
  450. for idx in 2..args.len-1:
  451. params.add(args[idx])
  452. call.add(args[idx][0])
  453. body.add(newNimNode(nnkEmpty),
  454. rawProc[1],
  455. rawProc[2],
  456. params,
  457. rawProc[4],
  458. rawProc[5],
  459. newTree(nnkStmtList, thisQuote, call)
  460. )
  461. result = body