paul@1007 | 1 | #!/usr/bin/env python |
paul@1007 | 2 | |
paul@1007 | 3 | """ |
paul@1007 | 4 | Attribute access plan translation. |
paul@1007 | 5 | |
paul@1007 | 6 | Copyright (C) 2014-2018, 2023 Paul Boddie <paul@boddie.org.uk> |
paul@1007 | 7 | |
paul@1007 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@1007 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@1007 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@1007 | 11 | version. |
paul@1007 | 12 | |
paul@1007 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@1007 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@1007 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@1007 | 16 | details. |
paul@1007 | 17 | |
paul@1007 | 18 | You should have received a copy of the GNU General Public License along with |
paul@1007 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@1007 | 20 | """ |
paul@1007 | 21 | |
paul@1007 | 22 | from encoders import encode_access_location |
paul@1007 | 23 | |
paul@1007 | 24 | class AccessPlan: |
paul@1007 | 25 | |
paul@1007 | 26 | "An attribute access plan." |
paul@1007 | 27 | |
paul@1007 | 28 | def __init__(self, name, test, test_type, base, traversed, traversal_modes, |
paul@1007 | 29 | remaining, context, context_test, first_method, final_method, |
paul@1007 | 30 | origin, accessor_kinds): |
paul@1007 | 31 | |
paul@1007 | 32 | "Initialise the plan." |
paul@1007 | 33 | |
paul@1007 | 34 | # With instance attribute initialisers, the assignment below would be |
paul@1007 | 35 | # generated automatically. |
paul@1007 | 36 | |
paul@1007 | 37 | ( |
paul@1007 | 38 | self.name, self.test, self.test_type, self.base, |
paul@1007 | 39 | self.traversed, self.traversal_modes, self.remaining, |
paul@1007 | 40 | self.context, self.context_test, |
paul@1007 | 41 | self.first_method, self.final_method, |
paul@1007 | 42 | self.origin, self.accessor_kinds) = ( |
paul@1007 | 43 | |
paul@1007 | 44 | name, test, test_type, base, |
paul@1007 | 45 | traversed, traversal_modes, remaining, |
paul@1007 | 46 | context, context_test, |
paul@1007 | 47 | first_method, final_method, |
paul@1007 | 48 | origin, accessor_kinds) |
paul@1007 | 49 | |
paul@1007 | 50 | # Define the first attribute access and subsequent accesses. |
paul@1007 | 51 | |
paul@1007 | 52 | self.first_attrname = None |
paul@1007 | 53 | self.traversed_attrnames = traversed |
paul@1007 | 54 | self.traversed_attrname_modes = traversal_modes |
paul@1007 | 55 | self.remaining_attrnames = remaining |
paul@1007 | 56 | |
paul@1007 | 57 | if traversed: |
paul@1007 | 58 | self.first_attrname = traversed[0] |
paul@1007 | 59 | self.traversed_attrnames = traversed[1:] |
paul@1007 | 60 | self.traversed_attrname_modes = traversal_modes[1:] |
paul@1007 | 61 | elif remaining: |
paul@1007 | 62 | self.first_attrname = remaining[0] |
paul@1007 | 63 | self.remaining_attrnames = remaining[1:] |
paul@1007 | 64 | |
paul@1007 | 65 | def access_first_attribute(self): |
paul@1007 | 66 | |
paul@1007 | 67 | "Return whether the first attribute is to be accessed." |
paul@1007 | 68 | |
paul@1007 | 69 | return self.final_method in ("access", "access-invoke", "assign") or \ |
paul@1007 | 70 | self.all_subsequent_attributes() |
paul@1007 | 71 | |
paul@1007 | 72 | def assigning_first_attribute(self): |
paul@1007 | 73 | |
paul@1007 | 74 | "Return whether the first attribute access involves assignment." |
paul@1007 | 75 | |
paul@1007 | 76 | return not self.all_subsequent_attributes() and self.final_method == "assign" |
paul@1007 | 77 | |
paul@1007 | 78 | def get_first_attribute_name(self): |
paul@1007 | 79 | |
paul@1007 | 80 | "Return any first attribute name to be used in an initial access." |
paul@1007 | 81 | |
paul@1007 | 82 | return self.first_attrname |
paul@1007 | 83 | |
paul@1007 | 84 | def all_subsequent_attributes(self): |
paul@1007 | 85 | |
paul@1007 | 86 | "Return all subsequent attribute names involved in accesses." |
paul@1007 | 87 | |
paul@1007 | 88 | return self.traversed_attrnames + self.remaining_attrnames |
paul@1007 | 89 | |
paul@1007 | 90 | def attribute_traversals(self): |
paul@1007 | 91 | |
paul@1007 | 92 | "Return a collection of (attribute name, traversal mode) tuples." |
paul@1007 | 93 | |
paul@1007 | 94 | return zip(self.traversed_attrnames, self.traversed_attrname_modes) |
paul@1007 | 95 | |
paul@1007 | 96 | def stored_accessor(self): |
paul@1007 | 97 | |
paul@1007 | 98 | "Return the variable used to obtain the accessor." |
paul@1007 | 99 | |
paul@1007 | 100 | return self.assigning_first_attribute() and "<target_accessor>" or "<accessor>" |
paul@1007 | 101 | |
paul@1007 | 102 | def stored_accessor_modifier(self): |
paul@1007 | 103 | |
paul@1007 | 104 | "Return the variable used to set the accessor." |
paul@1007 | 105 | |
paul@1007 | 106 | return self.assigning_first_attribute() and "<set_target_accessor>" or "<set_accessor>" |
paul@1007 | 107 | |
paul@1007 | 108 | def get_original_accessor(self): |
paul@1007 | 109 | |
paul@1007 | 110 | "Return the original accessor details." |
paul@1007 | 111 | |
paul@1007 | 112 | # Identify any static original accessor. |
paul@1007 | 113 | |
paul@1007 | 114 | if self.base: |
paul@1007 | 115 | return self.base |
paul@1007 | 116 | |
paul@1007 | 117 | # Employ names as contexts unless the context needs testing and |
paul@1007 | 118 | # potentially updating. In such cases, temporary context storage is |
paul@1007 | 119 | # used instead. |
paul@1007 | 120 | |
paul@1007 | 121 | elif self.name and not (self.context_test == "test" and |
paul@1007 | 122 | self.final_method in ("access-invoke", "static-invoke")): |
paul@1007 | 123 | |
paul@1007 | 124 | return "<name>" |
paul@1007 | 125 | |
paul@1007 | 126 | # Use a generic placeholder representing the access expression in |
paul@1007 | 127 | # the general case. |
paul@1007 | 128 | |
paul@1007 | 129 | else: |
paul@1007 | 130 | return "<expr>" |
paul@1007 | 131 | |
paul@1007 | 132 | def get_instructions(self): |
paul@1007 | 133 | |
paul@1007 | 134 | "Return a list of instructions corresponding to the plan." |
paul@1007 | 135 | |
paul@1007 | 136 | # Emit instructions by appending them to a list. |
paul@1007 | 137 | |
paul@1007 | 138 | instructions = [] |
paul@1007 | 139 | emit = instructions.append |
paul@1007 | 140 | |
paul@1007 | 141 | # Set up any initial instructions. |
paul@1007 | 142 | |
paul@1007 | 143 | accessor, context = self.process_initialisation(emit) |
paul@1007 | 144 | |
paul@1007 | 145 | # Apply any test. |
paul@1007 | 146 | |
paul@1007 | 147 | if self.test[0] == "test": |
paul@1007 | 148 | test_accessor = accessor = ("__%s_%s_%s" % self.test, accessor, self.test_type) |
paul@1007 | 149 | else: |
paul@1007 | 150 | test_accessor = None |
paul@1007 | 151 | |
paul@1007 | 152 | # Perform the first or final access. |
paul@1007 | 153 | # The access only needs performing if the resulting accessor is used. |
paul@1007 | 154 | |
paul@1007 | 155 | accessor = self.process_first_attribute(accessor, emit) |
paul@1007 | 156 | |
paul@1007 | 157 | # Perform accesses for the traversed and remaining attributes. |
paul@1007 | 158 | |
paul@1007 | 159 | accessor, context = self.process_traversed_attributes(accessor, context, emit) |
paul@1007 | 160 | accessor, context = self.process_remaining_attributes(accessor, context, emit) |
paul@1007 | 161 | |
paul@1007 | 162 | # Make any accessor test available if not emitted. |
paul@1007 | 163 | |
paul@1007 | 164 | test_accessor = not instructions and test_accessor or None |
paul@1007 | 165 | |
paul@1007 | 166 | # Perform the access on the actual target. |
paul@1007 | 167 | |
paul@1007 | 168 | accessor = self.process_attribute_access(accessor, context, test_accessor, emit) |
paul@1007 | 169 | |
paul@1007 | 170 | # Produce an advisory instruction regarding the context. |
paul@1007 | 171 | |
paul@1007 | 172 | self.process_context_identity(context, emit) |
paul@1007 | 173 | |
paul@1007 | 174 | # Produce an advisory instruction regarding the final attribute. |
paul@1007 | 175 | |
paul@1007 | 176 | if self.origin: |
paul@1007 | 177 | emit(("<final_identity>", self.origin)) |
paul@1007 | 178 | |
paul@1007 | 179 | return instructions |
paul@1007 | 180 | |
paul@1007 | 181 | def process_initialisation(self, emit): |
paul@1007 | 182 | |
paul@1007 | 183 | """ |
paul@1007 | 184 | Use 'emit' to generate instructions for any initialisation of attribute |
paul@1007 | 185 | access. Return the potentially revised accessor and context indicators. |
paul@1007 | 186 | """ |
paul@1007 | 187 | |
paul@1007 | 188 | # Identify any static original accessor. |
paul@1007 | 189 | |
paul@1007 | 190 | original_accessor = self.get_original_accessor() |
paul@1007 | 191 | |
paul@1007 | 192 | # Determine whether the first access involves assignment. |
paul@1007 | 193 | |
paul@1007 | 194 | set_accessor = self.stored_accessor_modifier() |
paul@1007 | 195 | stored_accessor = self.stored_accessor() |
paul@1007 | 196 | |
paul@1007 | 197 | # Set the context if already available. |
paul@1007 | 198 | |
paul@1007 | 199 | context = None |
paul@1007 | 200 | |
paul@1007 | 201 | if self.context == "base": |
paul@1007 | 202 | accessor = context = (self.base,) |
paul@1007 | 203 | elif self.context == "original-accessor": |
paul@1007 | 204 | |
paul@1007 | 205 | # Prevent re-evaluation of any dynamic expression by storing it. |
paul@1007 | 206 | |
paul@1007 | 207 | if original_accessor == "<expr>": |
paul@1007 | 208 | if self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 209 | emit(("<set_context>", original_accessor)) |
paul@1007 | 210 | accessor = context = ("<context>",) |
paul@1007 | 211 | else: |
paul@1007 | 212 | emit((set_accessor, original_accessor)) |
paul@1007 | 213 | accessor = context = (stored_accessor,) |
paul@1007 | 214 | else: |
paul@1007 | 215 | accessor = context = (original_accessor,) |
paul@1007 | 216 | |
paul@1007 | 217 | # Assigning does not set the context. |
paul@1007 | 218 | |
paul@1007 | 219 | elif self.context in ("final-accessor", "unset") and self.access_first_attribute(): |
paul@1007 | 220 | |
paul@1007 | 221 | # Prevent re-evaluation of any dynamic expression by storing it. |
paul@1007 | 222 | |
paul@1007 | 223 | if original_accessor == "<expr>": |
paul@1007 | 224 | emit((set_accessor, original_accessor)) |
paul@1007 | 225 | accessor = (stored_accessor,) |
paul@1007 | 226 | else: |
paul@1007 | 227 | accessor = (original_accessor,) |
paul@1007 | 228 | else: |
paul@1007 | 229 | accessor = None |
paul@1007 | 230 | |
paul@1007 | 231 | return accessor, context |
paul@1007 | 232 | |
paul@1007 | 233 | def process_first_attribute(self, accessor, emit): |
paul@1007 | 234 | |
paul@1007 | 235 | """ |
paul@1007 | 236 | Using 'accessor', use 'emit' to generate instructions for any first |
paul@1007 | 237 | attribute access. Return the potentially revised accessor. |
paul@1007 | 238 | """ |
paul@1007 | 239 | |
paul@1007 | 240 | if self.access_first_attribute(): |
paul@1007 | 241 | attrname = self.get_first_attribute_name() |
paul@1007 | 242 | assigning = self.assigning_first_attribute() |
paul@1007 | 243 | |
paul@1007 | 244 | if self.first_method == "relative-class": |
paul@1007 | 245 | if assigning: |
paul@1007 | 246 | emit(("__store_via_class", accessor, attrname, "<assexpr>")) |
paul@1007 | 247 | else: |
paul@1007 | 248 | accessor = ("__load_via_class", accessor, attrname) |
paul@1007 | 249 | |
paul@1007 | 250 | elif self.first_method == "relative-object": |
paul@1007 | 251 | if assigning: |
paul@1007 | 252 | emit(("__store_via_object", accessor, attrname, "<assexpr>")) |
paul@1007 | 253 | else: |
paul@1007 | 254 | accessor = ("__load_via_object", accessor, attrname) |
paul@1007 | 255 | |
paul@1007 | 256 | elif self.first_method == "relative-object-class": |
paul@1007 | 257 | if assigning: |
paul@1007 | 258 | emit(("__get_class_and_store", accessor, attrname, "<assexpr>")) |
paul@1007 | 259 | else: |
paul@1007 | 260 | accessor = ("__get_class_and_load", accessor, attrname) |
paul@1007 | 261 | |
paul@1007 | 262 | elif self.first_method == "check-class": |
paul@1007 | 263 | if assigning: |
paul@1007 | 264 | emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>")) |
paul@1007 | 265 | else: |
paul@1007 | 266 | accessor = ("__check_and_load_via_class", accessor, attrname) |
paul@1007 | 267 | |
paul@1007 | 268 | elif self.first_method == "check-object": |
paul@1007 | 269 | if assigning: |
paul@1007 | 270 | emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>")) |
paul@1007 | 271 | else: |
paul@1007 | 272 | accessor = ("__check_and_load_via_object", accessor, attrname) |
paul@1007 | 273 | |
paul@1007 | 274 | elif self.first_method == "check-object-class": |
paul@1007 | 275 | if assigning: |
paul@1007 | 276 | emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) |
paul@1007 | 277 | else: |
paul@1007 | 278 | accessor = ("__check_and_load_via_any", accessor, attrname) |
paul@1007 | 279 | |
paul@1007 | 280 | return accessor |
paul@1007 | 281 | |
paul@1007 | 282 | def process_traversed_attributes(self, accessor, context, emit): |
paul@1007 | 283 | |
paul@1007 | 284 | """ |
paul@1007 | 285 | Using 'accessor' and 'context', use 'emit' to generate instructions |
paul@1007 | 286 | for the traversed attribute accesses. Return the potentially revised |
paul@1007 | 287 | accessor and context indicators. |
paul@1007 | 288 | """ |
paul@1007 | 289 | |
paul@1007 | 290 | # Traverse attributes using the accessor. |
paul@1007 | 291 | |
paul@1007 | 292 | num_remaining = len(self.all_subsequent_attributes()) |
paul@1007 | 293 | |
paul@1007 | 294 | if self.traversed_attrnames: |
paul@1007 | 295 | for attrname, traversal_mode in self.attribute_traversals(): |
paul@1007 | 296 | assigning = num_remaining == 1 and self.final_method == "assign" |
paul@1007 | 297 | |
paul@1007 | 298 | # Set the context, if appropriate. |
paul@1007 | 299 | |
paul@1007 | 300 | if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": |
paul@1007 | 301 | |
paul@1007 | 302 | # Invoked attributes employ a separate context accessed |
paul@1007 | 303 | # during invocation. |
paul@1007 | 304 | |
paul@1007 | 305 | if self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 306 | emit(("<set_context>", accessor)) |
paul@1007 | 307 | accessor = context = "<context>" |
paul@1007 | 308 | |
paul@1007 | 309 | # A private context within the access is otherwise |
paul@1007 | 310 | # retained. |
paul@1007 | 311 | |
paul@1007 | 312 | else: |
paul@1007 | 313 | emit(("<set_private_context>", accessor)) |
paul@1007 | 314 | accessor = context = "<private_context>" |
paul@1007 | 315 | |
paul@1007 | 316 | # Perform the access only if not achieved directly. |
paul@1007 | 317 | |
paul@1007 | 318 | if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): |
paul@1007 | 319 | |
paul@1007 | 320 | if traversal_mode == "class": |
paul@1007 | 321 | if assigning: |
paul@1007 | 322 | emit(("__store_via_class", accessor, attrname, "<assexpr>")) |
paul@1007 | 323 | else: |
paul@1007 | 324 | accessor = ("__load_via_class", accessor, attrname) |
paul@1007 | 325 | else: |
paul@1007 | 326 | if assigning: |
paul@1007 | 327 | emit(("__store_via_object", accessor, attrname, "<assexpr>")) |
paul@1007 | 328 | else: |
paul@1007 | 329 | accessor = ("__load_via_object", accessor, attrname) |
paul@1007 | 330 | |
paul@1007 | 331 | num_remaining -= 1 |
paul@1007 | 332 | |
paul@1007 | 333 | return accessor, context |
paul@1007 | 334 | |
paul@1007 | 335 | def process_remaining_attributes(self, accessor, context, emit): |
paul@1007 | 336 | |
paul@1007 | 337 | """ |
paul@1007 | 338 | Using 'accessor' and 'context', use 'emit' to generate instructions |
paul@1007 | 339 | for the remaining attribute accesses. Return the potentially revised |
paul@1007 | 340 | accessor and context indicators. |
paul@1007 | 341 | """ |
paul@1007 | 342 | |
paul@1007 | 343 | remaining = self.remaining_attrnames |
paul@1007 | 344 | |
paul@1007 | 345 | if remaining: |
paul@1007 | 346 | num_remaining = len(remaining) |
paul@1007 | 347 | |
paul@1007 | 348 | for attrname in remaining: |
paul@1007 | 349 | assigning = num_remaining == 1 and self.final_method == "assign" |
paul@1007 | 350 | |
paul@1007 | 351 | # Set the context, if appropriate. |
paul@1007 | 352 | |
paul@1007 | 353 | if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": |
paul@1007 | 354 | |
paul@1007 | 355 | # Invoked attributes employ a separate context accessed |
paul@1007 | 356 | # during invocation. |
paul@1007 | 357 | |
paul@1007 | 358 | if self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 359 | emit(("<set_context>", accessor)) |
paul@1007 | 360 | accessor = context = "<context>" |
paul@1007 | 361 | |
paul@1007 | 362 | # A private context within the access is otherwise |
paul@1007 | 363 | # retained. |
paul@1007 | 364 | |
paul@1007 | 365 | else: |
paul@1007 | 366 | emit(("<set_private_context>", accessor)) |
paul@1007 | 367 | accessor = context = "<private_context>" |
paul@1007 | 368 | |
paul@1007 | 369 | # Perform the access only if not achieved directly. |
paul@1007 | 370 | |
paul@1007 | 371 | if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): |
paul@1007 | 372 | |
paul@1007 | 373 | # Constrain instructions involving certain special |
paul@1007 | 374 | # attribute names. |
paul@1007 | 375 | |
paul@1007 | 376 | to_search = attrname == "__data__" and "object" or "any" |
paul@1007 | 377 | |
paul@1007 | 378 | if assigning: |
paul@1007 | 379 | emit(("__check_and_store_via_%s" % to_search, accessor, attrname, "<assexpr>")) |
paul@1007 | 380 | else: |
paul@1007 | 381 | accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname) |
paul@1007 | 382 | |
paul@1007 | 383 | num_remaining -= 1 |
paul@1007 | 384 | |
paul@1007 | 385 | return accessor, context |
paul@1007 | 386 | |
paul@1007 | 387 | def process_attribute_access(self, accessor, context, test_accessor, emit): |
paul@1007 | 388 | |
paul@1007 | 389 | """ |
paul@1007 | 390 | Using 'accessor','context' and any 'test_accessor' operation, use 'emit' |
paul@1007 | 391 | to generate instructions for the final attribute access. Return the |
paul@1007 | 392 | potentially revised accessor. |
paul@1007 | 393 | """ |
paul@1007 | 394 | |
paul@1007 | 395 | # Define or emit the means of accessing the actual target. |
paul@1007 | 396 | |
paul@1007 | 397 | if self.final_method in ("static", "static-assign", "static-invoke"): |
paul@1007 | 398 | |
paul@1007 | 399 | if test_accessor: |
paul@1007 | 400 | emit(test_accessor) |
paul@1007 | 401 | |
paul@1007 | 402 | # Assignments to known attributes. |
paul@1007 | 403 | |
paul@1007 | 404 | if self.final_method == "static-assign": |
paul@1007 | 405 | parent, attrname = self.origin.rsplit(".", 1) |
paul@1007 | 406 | emit(("__store_via_object", parent, attrname, "<assexpr>")) |
paul@1007 | 407 | |
paul@1007 | 408 | # Invoked attributes employ a separate context. |
paul@1007 | 409 | |
paul@1007 | 410 | elif self.final_method in ("static", "static-invoke"): |
paul@1007 | 411 | accessor = ("__load_static_ignore", self.origin) |
paul@1007 | 412 | |
paul@1007 | 413 | # Wrap accesses in context operations. |
paul@1007 | 414 | |
paul@1007 | 415 | if self.context_test == "test": |
paul@1007 | 416 | |
paul@1007 | 417 | # Test and combine the context with static attribute details. |
paul@1007 | 418 | |
paul@1007 | 419 | if self.final_method == "static": |
paul@1007 | 420 | emit(("__load_static_test", context, self.origin)) |
paul@1007 | 421 | |
paul@1007 | 422 | # Test the context, storing it separately if required for the |
paul@1007 | 423 | # immediately invoked static attribute. |
paul@1007 | 424 | |
paul@1007 | 425 | elif self.final_method == "static-invoke": |
paul@1007 | 426 | emit(("<test_context_static>", context, self.origin)) |
paul@1007 | 427 | |
paul@1007 | 428 | # Test the context, storing it separately if required for an |
paul@1007 | 429 | # immediately invoked attribute. |
paul@1007 | 430 | |
paul@1007 | 431 | elif self.final_method == "access-invoke": |
paul@1007 | 432 | emit(("<test_context_revert>", context, accessor)) |
paul@1007 | 433 | |
paul@1007 | 434 | # Test the context and update the attribute details if |
paul@1007 | 435 | # appropriate. |
paul@1007 | 436 | |
paul@1007 | 437 | else: |
paul@1007 | 438 | emit(("__test_context", context, accessor)) |
paul@1007 | 439 | |
paul@1007 | 440 | elif self.context_test == "replace": |
paul@1007 | 441 | |
paul@1007 | 442 | # Produce an object with updated context. |
paul@1007 | 443 | |
paul@1007 | 444 | if self.final_method == "static": |
paul@1007 | 445 | emit(("__load_static_replace", context, self.origin)) |
paul@1007 | 446 | |
paul@1007 | 447 | # Omit the context update operation where the target is static |
paul@1007 | 448 | # and the context is recorded separately. |
paul@1007 | 449 | |
paul@1007 | 450 | elif self.final_method == "static-invoke": |
paul@1007 | 451 | pass |
paul@1007 | 452 | |
paul@1007 | 453 | # If a separate context is used for an immediate invocation, |
paul@1007 | 454 | # produce the attribute details unchanged. |
paul@1007 | 455 | |
paul@1007 | 456 | elif self.final_method == "access-invoke": |
paul@1007 | 457 | emit(accessor) |
paul@1007 | 458 | |
paul@1007 | 459 | # Update the context in the attribute details. |
paul@1007 | 460 | |
paul@1007 | 461 | else: |
paul@1007 | 462 | emit(("__update_context", context, accessor)) |
paul@1007 | 463 | |
paul@1007 | 464 | # Omit the accessor for assignments and for invocations of static |
paul@1007 | 465 | # targets. Otherwise, emit the accessor which may involve the |
paul@1007 | 466 | # invocation of a test. |
paul@1007 | 467 | |
paul@1007 | 468 | elif self.final_method not in ("assign", "static-assign", "static-invoke"): |
paul@1007 | 469 | emit(accessor) |
paul@1007 | 470 | |
paul@1007 | 471 | return accessor |
paul@1007 | 472 | |
paul@1007 | 473 | def process_context_identity(self, context, emit): |
paul@1007 | 474 | |
paul@1007 | 475 | """ |
paul@1007 | 476 | Using 'context', use 'emit' to generate instructions to test the context |
paul@1007 | 477 | identity. |
paul@1007 | 478 | """ |
paul@1007 | 479 | |
paul@1007 | 480 | if context: |
paul@1007 | 481 | |
paul@1007 | 482 | # Only verify the context for invocation purposes if a suitable |
paul@1007 | 483 | # test has been performed. |
paul@1007 | 484 | |
paul@1007 | 485 | if self.context_test in ("ignore", "replace") or \ |
paul@1007 | 486 | self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 487 | |
paul@1007 | 488 | emit(("<context_identity_verified>", context)) |
paul@1007 | 489 | else: |
paul@1007 | 490 | emit(("<context_identity>", context)) |
paul@1007 | 491 | |
paul@1007 | 492 | def write(self, f, location): |
paul@1007 | 493 | |
paul@1007 | 494 | "Write the plan to file 'f' with the given 'location' information." |
paul@1007 | 495 | |
paul@1007 | 496 | print >>f, encode_access_location(location), \ |
paul@1007 | 497 | self.name or "{}", \ |
paul@1007 | 498 | self.test and "-".join(self.test) or "{}", \ |
paul@1007 | 499 | self.test_type or "{}", \ |
paul@1007 | 500 | self.base or "{}", \ |
paul@1007 | 501 | ".".join(self.traversed_attrnames) or "{}", \ |
paul@1007 | 502 | ".".join(self.traversed_attrname_modes) or "{}", \ |
paul@1007 | 503 | ".".join(self.remaining_attrnames) or "{}", \ |
paul@1007 | 504 | self.context, self.context_test, \ |
paul@1007 | 505 | self.first_method, self.final_method, self.origin or "{}", \ |
paul@1007 | 506 | ",".join(self.accessor_kinds) |
paul@1007 | 507 | |
paul@1007 | 508 | # vim: tabstop=4 expandtab shiftwidth=4 |