Worlds2_Library.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. // Copyright (c) 2008, 2010 Alessandro Warth <awarth@cs.ucla.edu>
  2. DEBUG = false;
  3. // getTag: object -> unique id
  4. // tagToRef: unique id -> object
  5. // Note: this hashing scheme causes a huge memory leak (all b/c JS doesn't support weak references)
  6. (function() {
  7. var numIdx = 0, tagToRef = {}
  8. var _getTag = function(r) {
  9. if (r === null || r === undefined)
  10. return r
  11. switch (typeof r) {
  12. case "boolean": return r == true ? "Btrue" : "Bfalse"
  13. case "string": return "S" + r
  14. case "number": return "N" + r
  15. default: return r.hasOwnProperty("_id_") ? r._id_ : r._id_ = "R" + numIdx++
  16. }
  17. }
  18. getTag = function(r) {
  19. var tag = _getTag(r)
  20. tagToRef[tag] = r
  21. return tag
  22. }
  23. getRef = function(t) {
  24. return tagToRef[t]
  25. }
  26. })()
  27. // implementation of possible worlds
  28. worldProto = {}
  29. baseWorld = thisWorld = (function() {
  30. var writes = {}
  31. return {
  32. parent: worldProto,
  33. writes: writes,
  34. hasOwn: function(r, p) {
  35. var id = getTag(r)
  36. return writes.hasOwnProperty(id) && writes[id].hasOwnProperty(p)
  37. },
  38. has: function(r, p) {
  39. return this.hasOwn(r, p) ||
  40. r !== Object.prototype && this.has(r === null || r === undefined ? Object.prototype : r.parent, p)
  41. },
  42. props: function(r, ps) {
  43. var id = getTag(r)
  44. if (writes.hasOwnProperty(id))
  45. for (var p in writes[id])
  46. if (writes[id].hasOwnProperty(p))
  47. ps[p] = true
  48. if (r !== Object.prototype)
  49. this.props(r === null || r === undefined ? Object.prototype : r.parent, ps)
  50. return ps
  51. },
  52. _get: function(r, p) {
  53. var id = getTag(r)
  54. if (DEBUG) console.log("? top-level world looking up " + id + "." + p)
  55. if (writes.hasOwnProperty(id) && writes[id].hasOwnProperty(p))
  56. return writes[id][p]
  57. else if (r !== Object.prototype)
  58. return thisWorld._get(r === null || r === undefined ? Object.prototype : r.parent, p)
  59. else
  60. return undefined
  61. },
  62. get: function(r, p) {
  63. // the top-level world's commit operation is a no-op, so reads don't have to be recorded
  64. if (typeof r === "string" && (typeof p === "number" || p === "length"))
  65. return r[p]
  66. else
  67. return this._get(r, p)
  68. },
  69. set: function(r, p, v) {
  70. if (typeof r === "string" && (typeof p === "number" || p === "length"))
  71. throw "the indices and length of a string are immutable, and you tried to change them!"
  72. var id = getTag(r)
  73. if (DEBUG) console.log("! top-level world assigning to " + id + "." + p)
  74. if (!writes.hasOwnProperty(id))
  75. writes[id] = {}
  76. writes[id][p] = v
  77. return v
  78. },
  79. commit: function() { },
  80. sprout: function() {
  81. var parentWorld = this, writes = {}, reads = {}
  82. return {
  83. parent: worldProto,
  84. writes: writes,
  85. reads: reads,
  86. hasOwn: function(r, p) {
  87. var id = getTag(r)
  88. return writes.hasOwnProperty(id) && writes[id].hasOwnProperty(p) ||
  89. reads.hasOwnProperty(id) && reads[id].hasOwnProperty(p) ||
  90. parentWorld.hasOwn(r, p)
  91. },
  92. has: function(r, p) {
  93. return this.hasOwn(r, p) ||
  94. r !== Object.prototype && this.has(r === null || r === undefined ? Object.prototype : r.parent, p)
  95. },
  96. props: function(r, ps) {
  97. var id = getTag(r)
  98. if (writes.hasOwnProperty(id))
  99. for (var p in writes[id])
  100. if (writes[id].hasOwnProperty(p))
  101. ps[p] = true
  102. if (reads.hasOwnProperty(id))
  103. for (var p in reads[id])
  104. if (reads[id].hasOwnProperty(p))
  105. ps[p] = true
  106. if (r !== Object.prototype)
  107. this.props(r === null || r === undefined ? Object.prototype : r.parent, ps)
  108. parentWorld.props(r, ps)
  109. return ps
  110. },
  111. _get: function(r, p) {
  112. var id = getTag(r)
  113. if (DEBUG) console.log("? child world looking up " + id + "." + p)
  114. if (writes.hasOwnProperty(id) && writes[id].hasOwnProperty(p))
  115. return writes[id][p]
  116. else if (reads.hasOwnProperty(id) && reads[id].hasOwnProperty(p))
  117. return reads[id][p]
  118. else
  119. return parentWorld._get(r, p)
  120. },
  121. get: function(r, p) {
  122. if (typeof r === "string" && (typeof p === "number" || p === "length"))
  123. return r[p]
  124. var id = getTag(r), ans = this._get(r, p)
  125. if (!reads.hasOwnProperty(id)) {
  126. reads[id] = {}
  127. if (!reads[id].hasOwnProperty(p))
  128. reads[id][p] = ans
  129. }
  130. return ans
  131. },
  132. set: function(r, p, v) {
  133. if (typeof r === "string" && (typeof p === "number" || p === "length"))
  134. throw "the indices and length of a string are immutable, and you tried to change them!"
  135. var id = getTag(r)
  136. if (DEBUG) console.log("! child world assigning to " + id + "." + p)
  137. if (!writes.hasOwnProperty(id))
  138. writes[id] = {}
  139. writes[id][p] = v
  140. return v
  141. },
  142. commit: function() {
  143. // serializability check
  144. for (var id in reads) {
  145. if (!reads.hasOwnProperty(id))
  146. continue
  147. for (var p in reads[id]) {
  148. if (!reads[id].hasOwnProperty(p))
  149. continue
  150. else if (reads[id][p] !== parentWorld._get(getRef(id), p))
  151. throw "commit failed"
  152. }
  153. }
  154. // propagation of side effects
  155. for (var id in writes) {
  156. if (!writes.hasOwnProperty(id))
  157. continue
  158. for (var p in writes[id]) {
  159. if (!writes[id].hasOwnProperty(p))
  160. continue
  161. if (!parentWorld.writes.hasOwnProperty(id))
  162. parentWorld.writes[id] = {}
  163. if (DEBUG) console.log("committing " + id + "." + p)
  164. parentWorld.writes[id][p] = writes[id][p]
  165. }
  166. }
  167. writes = {}
  168. reads = {}
  169. },
  170. sprout: parentWorld.sprout
  171. }
  172. }
  173. }
  174. })()
  175. worldStack = [thisWorld]
  176. // Lexical scopes
  177. GlobalScope = function() { }
  178. GlobalScope.prototype = {
  179. parent: Object.prototype,
  180. hasOwn: function(n) { return thisWorld.hasOwn(this, n) },
  181. has: function(n) { return thisWorld.has(this, n) },
  182. get: function(n) { return thisWorld.get(this, n) },
  183. set: function(n, v) { return thisWorld.set(this, n, v) },
  184. decl: function(n, v) { return baseWorld.set(this, n, v) },
  185. makeChild: function() { return new ActivationRecord(this) }
  186. }
  187. ActivationRecord = function(parent) { this.parent = parent }
  188. ActivationRecord.prototype = new GlobalScope()
  189. ActivationRecord.prototype.get = function(n) { return thisWorld.has(this, n) ?
  190. thisWorld.get(this, n) :
  191. this.parent.get(n) }
  192. ActivationRecord.prototype.set = function(n, v) { return thisWorld.has(this, n) ?
  193. thisWorld.set(this, n, v) :
  194. this.parent.set(n, v) }
  195. thisScope = new GlobalScope()
  196. // Sends
  197. send = function(sel, recv, args) {
  198. //alert("doing a send, sel=" + sel + ", recv=" + recv + ", args=" + args)
  199. return thisWorld.get(recv, sel).apply(recv, args)
  200. }
  201. // New
  202. Function.prototype.worldsNew = function() {
  203. var r = {parent: thisWorld.get(this, "prototype")}
  204. this.apply(r, arguments)
  205. return r
  206. }
  207. // instanceof
  208. instanceOf = function(x, C) {
  209. var p = x.parent, Cp = thisWorld.get(C, "prototype")
  210. while (p != undefined) {
  211. if (p == Cp)
  212. return true
  213. p = p.parent
  214. }
  215. return false
  216. }
  217. // Some globals, etc.
  218. wObject = function() { }
  219. thisScope.decl("Object", wObject)
  220. thisWorld.set(wObject, "prototype", Object.prototype)
  221. thisWorld.set(Object.prototype, "hasOwn", function(p) { return thisWorld.has(this, p) })
  222. thisWorld.set(Object.prototype, "toString", function() { return "" + this })
  223. thisWorld.set(worldProto, "sprout", function() { return this.sprout() })
  224. thisWorld.set(worldProto, "commit", function() { return this.commit() })
  225. thisWorld.set(worldProto, "toString", function() { return "[World " + this.getTag() + "]" })
  226. wWorld = function() { }; thisScope.decl("World", wWorld); thisWorld.set(wWorld, "prototype", worldProto)
  227. wBoolean = function() { }; thisScope.decl("Boolean", wBoolean); thisWorld.set(wBoolean, "prototype", {parent: Object.prototype})
  228. wNumber = function() { }; thisScope.decl("Number", wNumber); thisWorld.set(wNumber, "prototype", {parent: Object.prototype})
  229. wString = function() { }; thisScope.decl("String", wString); thisWorld.set(wString, "prototype", {parent: Object.prototype})
  230. wArray = function() { }; thisScope.decl("Array", wArray); thisWorld.set(wArray, "prototype", {parent: Object.prototype})
  231. wFunction = function() { }; thisScope.decl("Function", wFunction); thisWorld.set(wFunction, "prototype", {parent: Object.prototype})
  232. Boolean.prototype.parent = thisWorld.get(wBoolean, "prototype")
  233. Number.prototype.parent = thisWorld.get(wNumber, "prototype")
  234. String.prototype.parent = thisWorld.get(wString, "prototype")
  235. Function.prototype.parent = thisWorld.get(wFunction, "prototype")
  236. // Don't need to do this for Array because Worlds/JS arrays are not JS arrays
  237. thisWorld.set(wString, "fromCharCode", function(x) { return String.fromCharCode(x) })
  238. thisWorld.set(String.prototype.parent, "charCodeAt", function(x) { return this.charCodeAt(x) })
  239. thisWorld.set(Function.prototype.parent, "apply", function(recv, args) {
  240. var jsArgs
  241. if (args && thisWorld.get(args, "length") > 0) {
  242. jsArgs = []
  243. for (var idx = 0; idx < thisWorld.get(args, "length"); idx++)
  244. jsArgs.push(thisWorld.get(args, idx))
  245. }
  246. return this.apply(recv, jsArgs)
  247. })
  248. thisWorld.set(Function.prototype.parent, "call", function(recv) {
  249. var jsArgs = []
  250. for (var idx = 1; idx < arguments.length; idx++)
  251. jsArgs.push(arguments[idx])
  252. return this.apply(recv, jsArgs)
  253. })
  254. thisScope.decl("null", null)
  255. thisScope.decl("undefined", undefined)
  256. thisScope.decl("true", true)
  257. thisScope.decl("false", false)
  258. thisScope.decl("jsEval", function(s) { return eval(thisWorld.get(s, "toString").call(s)) })
  259. thisScope.decl("print", function(s) { print(thisWorld.get(s, "toString").call(s)) })
  260. thisScope.decl("alert", function(s) { alert(thisWorld.get(s, "toString").call(s)) })
  261. thisScope.decl("prompt", function(s) { return prompt(thisWorld.get(s, "toString").call(s)) })
  262. thisScope.decl("confirm", function(s) { return confirm(thisWorld.get(s, "toString").call(s)) })
  263. thisScope.decl("parseInt", function(s) { return parseInt(s) })
  264. thisScope.decl("parseFloat", function(s) { return parseFloat(s) })
  265. WorldsConsole = {}
  266. thisScope.decl("console", WorldsConsole)
  267. thisWorld.set(WorldsConsole, "log", function(s) { Transcript.show(thisWorld.get(s, "toString").apply(s)) })
  268. Array.prototype.toWJSArray = function() {
  269. var r = wArray.worldsNew()
  270. for (var idx = 0; idx < this.length; idx++)
  271. thisWorld.set(r, idx, this[idx])
  272. thisWorld.set(r, "length", this.length)
  273. return r
  274. }
  275. Object.prototype.toWJSObject = function() {
  276. var r = wObject.worldsNew()
  277. for (var p in this)
  278. if (this.hasOwnProperty(p))
  279. thisWorld.set(r, p, this[p])
  280. return r
  281. }