about summary refs log tree commit diff stats
path: root/worker/maildir/container.go
diff options
context:
space:
mode:
authorJeff Martin <jeffmartin@gmail.com>2020-08-21 11:58:30 -0700
committerReto Brunner <reto@labrat.space>2020-08-31 22:00:28 +0200
commit0acb28645fe73d488b82d2915add6c262028bbe3 (patch)
treecd197771f4172e2da75c9fec6c5862cd377d7b51 /worker/maildir/container.go
parent43c4f2f3ab8bd914ce1b5c1841724f3c55550e9c (diff)
downloadaerc-0acb28645fe73d488b82d2915add6c262028bbe3.tar.gz
handle message unknown charset error
This change handles message parse errors by printing the error when the
user tries to view the message. Specifically only handling unknown
charset errors in this patch, but there are many types of invalid
messages that can be handled in this way.

aerc currently leaves certain messages in the msglist in the pending
(spinner) state, and I'm unable to view or modify the message. aerc also
only prints parse errors with message when they are initially loaded.
This UX is a little better, because you can still see the header info
about the message, and if you try to view it, you will see the specific
error.
Diffstat (limited to 'worker/maildir/container.go')
0 files changed, 0 insertions, 0 deletions
90'>90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413




























































































































































































































































































































































































































                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 034address.cc</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 12pt; font-size: 1em; }
.Constant { color: #00a0a0; }
.cSpecial { color: #008000; }
.traceContains { color: #008000; }
.Comment { color: #9090ff; }
.Delimiter { color: #800080; }
.Special { color: #c00000; }
.Identifier { color: #fcb165; }
.Normal { color: #eeeeee; background-color: #080808; padding-bottom: 1px; }
.CommentedCode { color: #6c6c6c; }
-->
</style>

<script type='text/javascript'>
<!--

-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment">//: Addresses help us spend less time copying data around.</span>

<span class="Comment">//: So far we've been operating on primitives like numbers and characters, and</span>
<span class="Comment">//: we've started combining these primitives together into larger logical</span>
<span class="Comment">//: units (containers or arrays) that may contain many different primitives at</span>
<span class="Comment">//: once. Containers and arrays can grow quite large in complex programs, and</span>
<span class="Comment">//: we'd like some way to efficiently share them between recipes without</span>
<span class="Comment">//: constantly having to make copies. Right now 'next-ingredient' and 'reply'</span>
<span class="Comment">//: copy data across recipe boundaries. To avoid copying large quantities of</span>
<span class="Comment">//: data around, we'll use *addresses*. An address is a bookmark to some</span>
<span class="Comment">//: arbitrary quantity of data (the *payload*). It's a primitive, so it's as</span>
<span class="Comment">//: efficient to copy as a number. To read or modify the payload 'pointed to'</span>
<span class="Comment">//: by an address, we'll perform a *lookup*.</span>
<span class="Comment">//:</span>
<span class="Comment">//: The notion of 'lookup' isn't an instruction like 'add' or 'subtract'.</span>
<span class="Comment">//: Instead it's an operation that can be performed when reading any of the</span>
<span class="Comment">//: ingredients of an instruction, and when writing to any of the products. To</span>
<span class="Comment">//: write to the payload of an ingredient rather than its value, simply add</span>
<span class="Comment">//: the /lookup property to it. Modern computers provide efficient support for</span>
<span class="Comment">//: addresses and lookups, making this a realistic feature.</span>
<span class="Comment">//:</span>
<span class="Comment">//: To recap: an address is a bookmark to some potentially large payload, and</span>
<span class="Comment">//: you can replace any ingredient or product with a lookup to an address of</span>
<span class="Comment">//: the appropriate type. But how do we get addresses to begin with? That</span>
<span class="Comment">//: requires a little more explanation. Once we introduce the notion of</span>
<span class="Comment">//: bookmarks to data, we have to think about the life cycle of a piece of</span>
<span class="Comment">//: data and its bookmarks (because remember, bookmarks can be copied around</span>
<span class="Comment">//: just like anything else). Otherwise several bad outcomes can result (and</span>
<span class="Comment">//: indeed *have* resulted in past languages like C):</span>
<span class="Comment">//:</span>
<span class="Comment">//:   a) You can run out of memory if you don't have a way to reclaim</span>
<span class="Comment">//:   data.</span>
<span class="Comment">//:   b) If you allow data to be reclaimed, you have to be careful not to</span>
<span class="Comment">//:   leave any stale addresses pointing at it. Otherwise your program might</span>
<span class="Comment">//:   try to lookup such an address and find something unexpected. Such</span>
<span class="Comment">//:   problems can be very hard to track down, and they can also be exploited</span>
<span class="Comment">//:   to break into your computer over the network, etc.</span>
<span class="Comment">//:</span>
<span class="Comment">//: To avoid these problems, we introduce the notion of a *reference count* or</span>
<span class="Comment">//: refcount. The life cycle of a bit of data accessed through addresses looks</span>
<span class="Comment">//: like this.</span>
<span class="Comment">//:</span>
<span class="Comment">//:    We create space in computer memory for it using the 'new' instruction.</span>
<span class="Comment">//:    The 'new' instruction takes a type as an ingredient, allocates</span>
<span class="Comment">//:    sufficient space to hold that type, and returns an address (bookmark)</span>
<span class="Comment">//:    to the allocated space.</span>
<span class="Comment">//:</span>
<span class="Comment">//:      x:address:number &lt;- new number:type</span>
<span class="Comment">//:</span>
<span class="Comment">//:                     +------------+</span>
<span class="Comment">//:          x -------&gt; |  number    |</span>
<span class="Comment">//:                     +------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    That isn't entirely accurate. Under the hood, 'new' allocates an extra</span>
<span class="Comment">//:    number -- the refcount:</span>
<span class="Comment">//:</span>
<span class="Comment">//:                     +------------+------------+</span>
<span class="Comment">//:          x -------&gt; | refcount   |  number    |</span>
<span class="Comment">//:                     +------------+------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    This probably seems like a waste of space. In practice it isn't worth</span>
<span class="Comment">//:    allocating individual numbers and our payload will tend to be larger,</span>
<span class="Comment">//:    so the picture would look more like this (zooming out a bit):</span>
<span class="Comment">//:</span>
<span class="Comment">//:                         +-------------------------+</span>
<span class="Comment">//:                     +---+                         |</span>
<span class="Comment">//:          x -------&gt; | r |                         |</span>
<span class="Comment">//:                     +---+        DATA             |</span>
<span class="Comment">//:                         |                         |</span>
<span class="Comment">//:                         |                         |</span>
<span class="Comment">//:                         +-------------------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    (Here 'r' denotes the refcount. It occupies a tiny amount of space</span>
<span class="Comment">//:    compared to the payload.)</span>
<span class="Comment">//:</span>
<span class="Comment">//:    Anyways, back to our example where the data is just a single number.</span>
<span class="Comment">//:    After the call to 'new', Mu's map of memory looks like this:</span>
<span class="Comment">//:</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:          x -------&gt; | 1 |  number    |</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    The refcount of 1 here indicates that this number has one bookmark</span>
<span class="Comment">//:    outstanding. If you then make a copy of x, the refcount increments:</span>
<span class="Comment">//:</span>
<span class="Comment">//:      y:address:number &lt;- copy x</span>
<span class="Comment">//:</span>
<span class="Comment">//:          x ---+     +---+------------+</span>
<span class="Comment">//:               +---&gt; | 2 |  number    |</span>
<span class="Comment">//:          y ---+     +---+------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    Whether you access the payload through x or y, Mu knows how many</span>
<span class="Comment">//:    bookmarks are outstanding to it. When you change x or y, the refcount</span>
<span class="Comment">//:    transparently decrements:</span>
<span class="Comment">//:</span>
<span class="Comment">//:      x &lt;- copy 0  # an address is just a number, you can always write 0 to it</span>
<span class="Comment">//:</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:          y -------&gt; | 1 |  number    |</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//:    The final flourish is what happens when the refcount goes down to 0: Mu</span>
<span class="Comment">//:    reclaims the space occupied by both refcount and payload in memory, and</span>
<span class="Comment">//:    they're ready to be reused by later calls to 'new'.</span>
<span class="Comment">//:</span>
<span class="Comment">//:      y &lt;- copy 0</span>
<span class="Comment">//:</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:                     | 0 |  XXXXXXX   |</span>
<span class="Comment">//:                     +---+------------+</span>
<span class="Comment">//:</span>
<span class="Comment">//: Using refcounts fixes both our problems a) and b) above: you can use</span>
<span class="Comment">//: memory for many different purposes as many times as you want without</span>
<span class="Comment">//: running out of memory, and you don't have to worry about ever leaving a</span>
<span class="Comment">//: dangling bookmark when you reclaim memory.</span>
<span class="Comment">//:</span>
<span class="Comment">//: This layer implements creating addresses using 'new'. The next few layers</span>
<span class="Comment">//: will flesh out the rest of the life cycle.</span>
<span class="Comment">//:</span>
<span class="Comment">//: The tests in this layer use unsafe operations so as to stay decoupled from</span>
<span class="Comment">//: 'new'.</span>

<span class="Comment">//: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack.</span>

<span class="Delimiter">:(scenario new)</span>
<span class="Comment"># call 'new' two times with identical types without modifying the results; you</span>
<span class="Comment"># should get back different results</span>
def main [
  <span class="Constant">1</span>:address:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type
  <span class="Constant">2</span>:address:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type
  <span class="Constant">3</span>:boolean/<span class="Special">raw &lt;- </span>equal <span class="Constant">1</span>:address:number/<span class="Special">raw</span><span class="Delimiter">,</span> <span class="Constant">2</span>:address:number/<span class="Special">raw</span>
]
<span class="traceContains">+mem: storing 0 in location 3</span>

<span class="Delimiter">:(scenario dilated_reagent_with_new)</span>
def main [
  <span class="Constant">1</span>:address:address:number<span class="Special"> &lt;- </span><span class="Normal">new</span> <span class="Delimiter">{(</span>address number<span class="Delimiter">)</span>: type<span class="Delimiter">}</span>
]
<span class="traceContains">+new: size of (&quot;address&quot; &quot;number&quot;) is 1</span>

<span class="Comment">//: 'new' takes a weird 'type' as its first ingredient; don't error on it</span>
<span class="Delimiter">:(before &quot;End Mu Types Initialization&quot;)</span>
put<span class="Delimiter">(</span>Type_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;type&quot;</span><span class="Delimiter">,</span> <span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">bool</span> is_mu_type_literal<span class="Delimiter">(</span><span class="Normal">const</span> reagent&amp; r<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  <span class="Identifier">return</span> is_literal<span class="Delimiter">(</span>r<span class="Delimiter">)</span> &amp;&amp; r<span class="Delimiter">.</span>type &amp;&amp; r<span class="Delimiter">.</span>type<span class="Delimiter">-&gt;</span>name == <span class="Constant">&quot;type&quot;</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(before &quot;End Primitive Recipe Declarations&quot;)</span>
NEW<span class="Delimiter">,</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Numbers&quot;)</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;new&quot;</span><span class="Delimiter">,</span> NEW<span class="Delimiter">);</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Checks&quot;)</span>
<span class="Normal">case</span> NEW: <span class="Delimiter">{</span>
  <span class="Normal">const</span> recipe&amp; caller = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">);</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>empty<span class="Delimiter">()</span> || SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> &gt; <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
    raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'new' requires one or two ingredients, but got '&quot;</span> &lt;&lt; to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
    <span class="Identifier">break</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Comment">// End NEW Check Special-cases</span>
  <span class="Normal">const</span> reagent&amp; type = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_type_literal<span class="Delimiter">(</span>type<span class="Delimiter">))</span> <span class="Delimiter">{</span>
    raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;first ingredient of 'new' should be a type, but got '&quot;</span> &lt;&lt; type<span class="Delimiter">.</span>original_string &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
    <span class="Identifier">break</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Delimiter">{</span>
    raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;result of 'new' should never be ignored</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
    <span class="Identifier">break</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>!product_of_new_is_valid<span class="Delimiter">(</span>inst<span class="Delimiter">))</span> <span class="Delimiter">{</span>
    raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;product of 'new' has incorrect type: '&quot;</span> &lt;&lt; to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
    <span class="Identifier">break</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">bool</span> product_of_new_is_valid<span class="Delimiter">(</span><span class="Normal">const</span> instruction&amp; inst<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> product = inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Comment">// Update NEW product in Check</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>!product<span class="Delimiter">.</span>type || product<span class="Delimiter">.</span>type<span class="Delimiter">-&gt;</span>value != get<span class="Delimiter">(</span>Type_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;address&quot;</span><span class="Delimiter">))</span>
    <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
  drop_from_type<span class="Delimiter">(</span>product<span class="Delimiter">,</span> <span class="Constant">&quot;address&quot;</span><span class="Delimiter">);</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> &gt; <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
    <span class="Comment">// array allocation</span>
    <span class="Normal">if</span> <span class="Delimiter">(</span>!product<span class="Delimiter">.</span>type || product<span class="Delimiter">.</span>type<span class="Delimiter">-&gt;</span>value != get<span class="Delimiter">(</span>Type_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;array&quot;</span><span class="Delimiter">))</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
    drop_from_type<span class="Delimiter">(</span>product<span class="Delimiter">,</span> <span class="Constant">&quot;array&quot;</span><span class="Delimiter">);</span>
  <span class="Delimiter">}</span>
  reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> expected_product<span class="Delimiter">(</span><span class="Constant">&quot;x:&quot;</span>+inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">);</span>
  <span class="Delimiter">{</span>
    string_tree* tmp_type_names = parse_string_tree<span class="Delimiter">(</span>expected_product<span class="Delimiter">.</span>type<span class="Delimiter">-&gt;</span>name<span class="Delimiter">);</span>
    <span class="Normal">delete</span> expected_product<span class="Delimiter">.</span>type<span class="Delimiter">;</span>
    expected_product<span class="Delimiter">.</span>type = new_type_tree<span class="Delimiter">(</span>tmp_type_names<span class="Delimiter">);</span>
    <span class="Normal">delete</span> tmp_type_names<span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Identifier">return</span> types_strictly_match<span class="Delimiter">(</span>product<span class="Delimiter">,</span> expected_product<span class="Delimiter">);</span>
<span class="Delimiter">}</span>

<span class="Normal">void</span> drop_from_type<span class="Delimiter">(</span>reagent&amp; r<span class="Delimiter">,</span> string expected_type<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>r<span class="Delimiter">.</span>type<span class="Delimiter">-&gt;</span>name != expected_type<span class="Delimiter">)</span> <span class="Delimiter">{</span>
    raise &lt;&lt; <span class="Constant">&quot;can't drop2 &quot;</span> &lt;&lt; expected_type &lt;&lt; <span class="Constant">&quot; from '&quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>r<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
    <span class="Identifier">return</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  type_tree* tmp = r<span class="Delimiter">.</span>type<span class="Delimiter">;</span>
  r<span class="Delimiter">.</span>type = tmp<span class="Delimiter">-&gt;</span>right<span class="Delimiter">;</span>
  tmp<span class="Delimiter">-&gt;</span>right = <span class="Constant">NULL</span><span class="Delimiter">;</span>
  <span class="Normal">delete</span> tmp<span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Comment">//: To implement 'new', a Mu transform turns all 'new' instructions into</span>
<span class="Comment">//: 'allocate' instructions that precompute the amount of memory they want to</span>
<span class="Comment">//: allocate.</span>

<span class="Comment">//: Ensure that we never call 'allocate' directly, and that there's no 'new'</span>
<span class="Comment">//: instructions left after the transforms have run.</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Checks&quot;)</span>
<span class="Normal">case</span> ALLOCATE: <span class="Delimiter">{</span>
  raise &lt;&lt; <span class="Constant">&quot;never call 'allocate' directly'; always use 'new'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
  <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Implementations&quot;)</span>
<span class="Normal">case</span> NEW: <span class="Delimiter">{</span>
  raise &lt;&lt; <span class="Constant">&quot;no implementation for 'new'; why wasn't it translated to 'allocate'? Please save a copy of your program and send it to Kartik.</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
  <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(after &quot;Transform.push_back(check_instruction)&quot;)</span>  <span class="Comment">// check_instruction will guard against direct 'allocate' instructions below</span>
Transform<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>transform_new_to_allocate<span class="Delimiter">);</span>  <span class="Comment">// idempotent</span>

<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> transform_new_to_allocate<span class="Delimiter">(</span><span class="Normal">const</span> recipe_ordinal r<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  trace<span class="Delimiter">(</span><span class="Constant">9991</span><span class="Delimiter">,</span> <span class="Constant">&quot;transform&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;--- convert 'new' to 'allocate' for recipe &quot;</span> &lt;&lt; get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name &lt;&lt; end<span class="Delimiter">();</span>
  <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>steps<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
    instruction&amp; inst = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>steps<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span>
    <span class="Comment">// Convert 'new' To 'allocate'</span>
    <span class="Normal">if</span> <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>name == <span class="Constant">&quot;new&quot;</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
      inst<span class="Delimiter">.</span>operation = ALLOCATE<span class="Delimiter">;</span>
      string_tree* type_name = <span class="Normal">new</span> string_tree<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">);</span>
      type_name = parse_string_tree<span class="Delimiter">(</span>type_name<span class="Delimiter">);</span>
      type_tree* type = new_type_tree<span class="Delimiter">(</span>type_name<span class="Delimiter">);</span>
      inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>set_value<span class="Delimiter">(</span>size_of<span class="Delimiter">(</span>type<span class="Delimiter">));</span>
      trace<span class="Delimiter">(</span><span class="Constant">9992</span><span class="Delimiter">,</span> <span class="Constant">&quot;new&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;size of &quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>type_name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot; is &quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>value &lt;&lt; end<span class="Delimiter">();</span>
      <span class="Normal">delete</span> type<span class="Delimiter">;</span>
      <span class="Normal">delete</span> type_name<span class="Delimiter">;</span>
    <span class="Delimiter">}</span>
  <span class="Delimiter">}</span>
<span class="Delimiter">}</span>

<span class="Comment">//: implement 'allocate' based on size</span>

<span class="Delimiter">:(before &quot;End Globals&quot;)</span>
<span class="Normal">const</span> <span class="Normal">int</span> Reserved_for_tests = <span class="Constant">1000</span><span class="Delimiter">;</span>
<span class="Normal">int</span> Memory_allocated_until = Reserved_for_tests<span class="Delimiter">;</span>
<span class="Normal">int</span> Initial_memory_per_routine = <span class="Constant">100000</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before &quot;End Setup&quot;)</span>
Memory_allocated_until = Reserved_for_tests<span class="Delimiter">;</span>
Initial_memory_per_routine = <span class="Constant">100000</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before &quot;End routine Fields&quot;)</span>
<span class="Normal">int</span> alloc<span class="Delimiter">,</span> alloc_max<span class="Delimiter">;</span>
<span class="Delimiter">:(before &quot;End routine Constructor&quot;)</span>
alloc = Memory_allocated_until<span class="Delimiter">;</span>
Memory_allocated_until += Initial_memory_per_routine<span class="Delimiter">;</span>
alloc_max = Memory_allocated_until<span class="Delimiter">;</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;new&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;routine allocated memory from &quot;</span> &lt;&lt; alloc &lt;&lt; <span class="Constant">&quot; to &quot;</span> &lt;&lt; alloc_max &lt;&lt; end<span class="Delimiter">();</span>

<span class="Delimiter">:(before &quot;End Primitive Recipe Declarations&quot;)</span>
ALLOCATE<span class="Delimiter">,</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Numbers&quot;)</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;allocate&quot;</span><span class="Delimiter">,</span> ALLOCATE<span class="Delimiter">);</span>
<span class="Delimiter">:(before &quot;End Primitive Recipe Implementations&quot;)</span>
<span class="Normal">case</span> ALLOCATE: <span class="Delimiter">{</span>
  <span class="Comment">// compute the space we need</span>
  <span class="Normal">int</span> size = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>ingredients<span class="Delimiter">)</span> &gt; <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
    <span class="Comment">// array allocation</span>
    trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;array size is &quot;</span> &lt;&lt; ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> &lt;&lt; end<span class="Delimiter">();</span>
    size = <span class="Comment">/*</span><span class="Comment">space for length</span><span class="Comment">*/</span><span class="Constant">1</span> + size*ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Delimiter">}</span>
  <span class="Comment">// include space for refcount</span>
  size++<span class="Delimiter">;</span>
  trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;allocating size &quot;</span> &lt;&lt; size &lt;&lt; end<span class="Delimiter">();</span>
<span class="CommentedCode">//?   Total_alloc += size;</span>
<span class="CommentedCode">//?   Num_alloc++;</span>
  <span class="Comment">// compute the region of memory to return</span>
  <span class="Comment">// really crappy at the moment</span>
  ensure_space<span class="Delimiter">(</span>size<span class="Delimiter">);</span>
  <span class="Normal">const</span> <span class="Normal">int</span> result = Current_routine<span class="Delimiter">-&gt;</span>alloc<span class="Delimiter">;</span>
  trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;new alloc: &quot;</span> &lt;&lt; result &lt;&lt; end<span class="Delimiter">();</span>
  <span class="Comment">// save result</span>
  products<span class="Delimiter">.</span>resize<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
  products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span>result<span class="Delimiter">);</span>
  <span class="Comment">// initialize allocated space</span>
  <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> address = result<span class="Delimiter">;</span> address &lt; result+size<span class="Delimiter">;</span> ++address<span class="Delimiter">)</span> <span class="Delimiter">{</span>
    trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;storing 0 in location &quot;</span> &lt;&lt; address &lt;&lt; end<span class="Delimiter">();</span>
    put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">,</span> <span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Delimiter">}</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">)</span> &gt; <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
    <span class="Comment">// initialize array length</span>
    trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;storing &quot;</span> &lt;&lt; ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot; in location &quot;</span> &lt;&lt; result+<span class="Comment">/*</span><span class="Comment">skip refcount</span><span class="Comment">*/</span><span class="Constant">1</span> &lt;&lt; end<span class="Delimiter">();</span>
    put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> result+<span class="Comment">/*</span><span class="Comment">skip refcount</span><span class="Comment">*/</span><span class="Constant">1</span><span class="Delimiter">,</span> ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
  <span class="Delimiter">}</span>
  Current_routine<span class="Delimiter">-&gt;</span>alloc += size<span class="Delimiter">;</span>
  <span class="Comment">// no support yet for reclaiming memory between routines</span>
  assert<span class="Delimiter">(</span>Current_routine<span class="Delimiter">-&gt;</span>alloc &lt;= Current_routine<span class="Delimiter">-&gt;</span>alloc_max<span class="Delimiter">);</span>
  <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Comment">//: statistics for debugging</span>
<span class="CommentedCode">//? :(before &quot;End Globals&quot;)</span>
<span class="CommentedCode">//? int Total_alloc = 0;</span>
<span class="CommentedCode">//? int Num_alloc = 0;</span>
<span class="CommentedCode">//? int Total_free = 0;</span>
<span class="CommentedCode">//? int Num_free = 0;</span>
<span class="CommentedCode">//? :(before &quot;End Setup&quot;)</span>
<span class="CommentedCode">//? Total_alloc = Num_alloc = Total_free = Num_free = 0;</span>
<span class="CommentedCode">//? :(before &quot;End Teardown&quot;)</span>
<span class="CommentedCode">//? cerr &lt;&lt; Total_alloc &lt;&lt; &quot;/&quot; &lt;&lt; Num_alloc</span>
<span class="CommentedCode">//?      &lt;&lt; &quot; vs &quot; &lt;&lt; Total_free &lt;&lt; &quot;/&quot; &lt;&lt; Num_free &lt;&lt; '\n';</span>
<span class="CommentedCode">//? cerr &lt;&lt; SIZE(Memory) &lt;&lt; '\n';</span>

<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> ensure_space<span class="Delimiter">(</span><span class="Normal">int</span> size<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>size &gt; Initial_memory_per_routine<span class="Delimiter">)</span> <span class="Delimiter">{</span>
    tb_shutdown<span class="Delimiter">();</span>
    cerr &lt;&lt; <span class="Constant">&quot;can't allocate &quot;</span> &lt;&lt; size &lt;&lt; <span class="Constant">&quot; locations, that's too much compared to &quot;</span> &lt;&lt; Initial_memory_per_routine &lt;&lt; <span class="Constant">&quot;.</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span><span class="Delimiter">;</span>
    exit<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
  <span class="Delimiter">}</span>
  <span class="Normal">if</span> <span class="Delimiter">(</span>Current_routine<span class="Delimiter">-&gt;</span>alloc + size &gt; Current_routine<span class="Delimiter">-&gt;</span>alloc_max<span class="Delimiter">)</span> <span class="Delimiter">{</span>
    <span class="Comment">// waste the remaining space and create a new chunk</span>
    Current_routine<span class="Delimiter">-&gt;</span>alloc = Memory_allocated_until<span class="Delimiter">;</span>
    Memory_allocated_until += Initial_memory_per_routine<span class="Delimiter">;</span>
    Current_routine<span class="Delimiter">-&gt;</span>alloc_max = Memory_allocated_until<span class="Delimiter">;</span>
    trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;new&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;routine allocated memory from &quot;</span> &lt;&lt; Current_routine<span class="Delimiter">-&gt;</span>alloc &lt;&lt; <span class="Constant">&quot; to &quot;</span> &lt;&lt; Current_routine<span class="Delimiter">-&gt;</span>alloc_max &lt;&lt; end<span class="Delimiter">();</span>
  <span class="Delimiter">}</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(scenario new_initializes)</span>
<span class="Special">% Memory_allocated_until = 10;</span>
<span class="Special">% put(Memory, Memory_allocated_until, 1);</span>
def main [
  <span class="Constant">1</span>:address:number<span class="Special"> &lt;- </span><span class="Normal">new</span> number:type
]
<span class="traceContains">+mem: storing 0 in location 10</span>

<span class="Delimiter">:(scenario new_array)</span>
def main [
  <span class="Constant">1</span>:address:array:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type<span class="Delimiter">,</span> <span class="Constant">5</span>
  <span class="Constant">2</span>:address:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type
  <span class="Constant">3</span>:number/<span class="Special">raw &lt;- </span>subtract <span class="Constant">2</span>:address:number/<span class="Special">raw</span><span class="Delimiter">,</span> <span class="Constant">1</span>:address:array:number/<span class="Special">raw</span>
]
<span class="traceContains">+run: {1: (&quot;address&quot; &quot;array&quot; &quot;number&quot;), &quot;raw&quot;: ()} &lt;- new {number: &quot;type&quot;}, {5: &quot;literal&quot;}</span>
<span class="traceContains">+mem: array size is 5</span>
<span class="Comment"># don't forget the extra location for array size, and the second extra location for the refcount</span>
<span class="traceContains">+mem: storing 7 in location 3</span>

<span class="Delimiter">:(scenario new_empty_array)</span>
def main [
  <span class="Constant">1</span>:address:array:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type<span class="Delimiter">,</span> <span class="Constant">0</span>
  <span class="Constant">2</span>:address:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type
  <span class="Constant">3</span>:number/<span class="Special">raw &lt;- </span>subtract <span class="Constant">2</span>:address:number/<span class="Special">raw</span><span class="Delimiter">,</span> <span class="Constant">1</span>:address:array:number/<span class="Special">raw</span>
]
<span class="traceContains">+run: {1: (&quot;address&quot; &quot;array&quot; &quot;number&quot;), &quot;raw&quot;: ()} &lt;- new {number: &quot;type&quot;}, {0: &quot;literal&quot;}</span>
<span class="traceContains">+mem: array size is 0</span>
<span class="Comment"># one location for array size, and one for the refcount</span>
<span class="traceContains">+mem: storing 2 in location 3</span>

<span class="Comment">//: If a routine runs out of its initial allocation, it should allocate more.</span>
<span class="Delimiter">:(scenario new_overflow)</span>
<span class="Special">% Initial_memory_per_routine = 3;  // barely enough room for point allocation below</span>
def main [
  <span class="Constant">1</span>:address:number/<span class="Special">raw &lt;- </span><span class="Normal">new</span> number:type
  <span class="Constant">2</span>:address:point/<span class="Special">raw &lt;- </span><span class="Normal">new</span> point:type  <span class="Comment"># not enough room in initial page</span>
]
<span class="traceContains">+new: routine allocated memory from 1000 to 1003</span>
<span class="traceContains">+new: routine allocated memory from 1003 to 1006</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->