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@1021 | 244 | store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" |
paul@1021 | 245 | |
paul@1009 | 246 | # Access via the accessor's class. |
paul@1009 | 247 | |
paul@1007 | 248 | if self.first_method == "relative-class": |
paul@1007 | 249 | if assigning: |
paul@1009 | 250 | emit(("<set_attr_ref>", ("__get_class_attr_ref", accessor, attrname))) |
paul@1021 | 251 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 252 | else: |
paul@1007 | 253 | accessor = ("__load_via_class", accessor, attrname) |
paul@1007 | 254 | |
paul@1009 | 255 | # Access via the accessor itself. |
paul@1009 | 256 | |
paul@1007 | 257 | elif self.first_method == "relative-object": |
paul@1007 | 258 | if assigning: |
paul@1009 | 259 | emit(("<set_attr_ref>", ("__get_object_attr_ref", accessor, attrname))) |
paul@1021 | 260 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 261 | else: |
paul@1007 | 262 | accessor = ("__load_via_object", accessor, attrname) |
paul@1007 | 263 | |
paul@1009 | 264 | # Access via a class accessor or the accessor's class. |
paul@1009 | 265 | |
paul@1007 | 266 | elif self.first_method == "relative-object-class": |
paul@1007 | 267 | if assigning: |
paul@1009 | 268 | emit(("__raise_type_error",)) |
paul@1007 | 269 | else: |
paul@1007 | 270 | accessor = ("__get_class_and_load", accessor, attrname) |
paul@1007 | 271 | |
paul@1009 | 272 | # Access via the accessor's class. |
paul@1009 | 273 | |
paul@1007 | 274 | elif self.first_method == "check-class": |
paul@1007 | 275 | if assigning: |
paul@1009 | 276 | emit(("__raise_type_error",)) |
paul@1007 | 277 | else: |
paul@1007 | 278 | accessor = ("__check_and_load_via_class", accessor, attrname) |
paul@1007 | 279 | |
paul@1009 | 280 | # Access via the accessor itself. |
paul@1009 | 281 | |
paul@1007 | 282 | elif self.first_method == "check-object": |
paul@1007 | 283 | if assigning: |
paul@1009 | 284 | emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname))) |
paul@1021 | 285 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 286 | else: |
paul@1007 | 287 | accessor = ("__check_and_load_via_object", accessor, attrname) |
paul@1007 | 288 | |
paul@1009 | 289 | # Access via a class accessor or the accessor's class. |
paul@1009 | 290 | # Here, only access via the accessor is supported. |
paul@1009 | 291 | |
paul@1007 | 292 | elif self.first_method == "check-object-class": |
paul@1007 | 293 | if assigning: |
paul@1009 | 294 | emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname))) |
paul@1021 | 295 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 296 | else: |
paul@1007 | 297 | accessor = ("__check_and_load_via_any", accessor, attrname) |
paul@1007 | 298 | |
paul@1007 | 299 | return accessor |
paul@1007 | 300 | |
paul@1007 | 301 | def process_traversed_attributes(self, accessor, context, emit): |
paul@1007 | 302 | |
paul@1007 | 303 | """ |
paul@1007 | 304 | Using 'accessor' and 'context', use 'emit' to generate instructions |
paul@1007 | 305 | for the traversed attribute accesses. Return the potentially revised |
paul@1007 | 306 | accessor and context indicators. |
paul@1007 | 307 | """ |
paul@1007 | 308 | |
paul@1007 | 309 | # Traverse attributes using the accessor. |
paul@1007 | 310 | |
paul@1007 | 311 | num_remaining = len(self.all_subsequent_attributes()) |
paul@1007 | 312 | |
paul@1007 | 313 | if self.traversed_attrnames: |
paul@1007 | 314 | for attrname, traversal_mode in self.attribute_traversals(): |
paul@1007 | 315 | assigning = num_remaining == 1 and self.final_method == "assign" |
paul@1007 | 316 | |
paul@1007 | 317 | # Set the context, if appropriate. |
paul@1007 | 318 | |
paul@1007 | 319 | if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": |
paul@1007 | 320 | |
paul@1007 | 321 | # Invoked attributes employ a separate context accessed |
paul@1007 | 322 | # during invocation. |
paul@1007 | 323 | |
paul@1007 | 324 | if self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 325 | emit(("<set_context>", accessor)) |
paul@1007 | 326 | accessor = context = "<context>" |
paul@1007 | 327 | |
paul@1007 | 328 | # A private context within the access is otherwise |
paul@1007 | 329 | # retained. |
paul@1007 | 330 | |
paul@1007 | 331 | else: |
paul@1007 | 332 | emit(("<set_private_context>", accessor)) |
paul@1007 | 333 | accessor = context = "<private_context>" |
paul@1007 | 334 | |
paul@1007 | 335 | # Perform the access only if not achieved directly. |
paul@1007 | 336 | |
paul@1007 | 337 | if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): |
paul@1007 | 338 | |
paul@1021 | 339 | store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" |
paul@1021 | 340 | |
paul@1007 | 341 | if traversal_mode == "class": |
paul@1007 | 342 | if assigning: |
paul@1009 | 343 | emit(("<set_attr_ref>", ("__get_class_attr_ref", accessor, attrname))) |
paul@1021 | 344 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 345 | else: |
paul@1007 | 346 | accessor = ("__load_via_class", accessor, attrname) |
paul@1007 | 347 | else: |
paul@1007 | 348 | if assigning: |
paul@1009 | 349 | emit(("<set_attr_ref>", ("__get_object_attr_ref", accessor, attrname))) |
paul@1021 | 350 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 351 | else: |
paul@1007 | 352 | accessor = ("__load_via_object", accessor, attrname) |
paul@1007 | 353 | |
paul@1007 | 354 | num_remaining -= 1 |
paul@1007 | 355 | |
paul@1007 | 356 | return accessor, context |
paul@1007 | 357 | |
paul@1007 | 358 | def process_remaining_attributes(self, accessor, context, emit): |
paul@1007 | 359 | |
paul@1007 | 360 | """ |
paul@1007 | 361 | Using 'accessor' and 'context', use 'emit' to generate instructions |
paul@1007 | 362 | for the remaining attribute accesses. Return the potentially revised |
paul@1007 | 363 | accessor and context indicators. |
paul@1007 | 364 | """ |
paul@1007 | 365 | |
paul@1007 | 366 | remaining = self.remaining_attrnames |
paul@1007 | 367 | |
paul@1007 | 368 | if remaining: |
paul@1007 | 369 | num_remaining = len(remaining) |
paul@1007 | 370 | |
paul@1007 | 371 | for attrname in remaining: |
paul@1007 | 372 | assigning = num_remaining == 1 and self.final_method == "assign" |
paul@1007 | 373 | |
paul@1007 | 374 | # Set the context, if appropriate. |
paul@1007 | 375 | |
paul@1007 | 376 | if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": |
paul@1007 | 377 | |
paul@1007 | 378 | # Invoked attributes employ a separate context accessed |
paul@1007 | 379 | # during invocation. |
paul@1007 | 380 | |
paul@1007 | 381 | if self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 382 | emit(("<set_context>", accessor)) |
paul@1007 | 383 | accessor = context = "<context>" |
paul@1007 | 384 | |
paul@1007 | 385 | # A private context within the access is otherwise |
paul@1007 | 386 | # retained. |
paul@1007 | 387 | |
paul@1007 | 388 | else: |
paul@1007 | 389 | emit(("<set_private_context>", accessor)) |
paul@1007 | 390 | accessor = context = "<private_context>" |
paul@1007 | 391 | |
paul@1007 | 392 | # Perform the access only if not achieved directly. |
paul@1007 | 393 | |
paul@1007 | 394 | if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): |
paul@1007 | 395 | |
paul@1007 | 396 | # Constrain instructions involving certain special |
paul@1007 | 397 | # attribute names. |
paul@1007 | 398 | |
paul@1021 | 399 | store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" |
paul@1009 | 400 | to_search = attrname != "__data__" and "any" or "object" |
paul@1007 | 401 | |
paul@1007 | 402 | if assigning: |
paul@1009 | 403 | emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname))) |
paul@1021 | 404 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 405 | else: |
paul@1007 | 406 | accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname) |
paul@1007 | 407 | |
paul@1007 | 408 | num_remaining -= 1 |
paul@1007 | 409 | |
paul@1007 | 410 | return accessor, context |
paul@1007 | 411 | |
paul@1007 | 412 | def process_attribute_access(self, accessor, context, test_accessor, emit): |
paul@1007 | 413 | |
paul@1007 | 414 | """ |
paul@1007 | 415 | Using 'accessor','context' and any 'test_accessor' operation, use 'emit' |
paul@1007 | 416 | to generate instructions for the final attribute access. Return the |
paul@1007 | 417 | potentially revised accessor. |
paul@1007 | 418 | """ |
paul@1007 | 419 | |
paul@1007 | 420 | # Define or emit the means of accessing the actual target. |
paul@1007 | 421 | |
paul@1007 | 422 | if self.final_method in ("static", "static-assign", "static-invoke"): |
paul@1007 | 423 | |
paul@1007 | 424 | if test_accessor: |
paul@1007 | 425 | emit(test_accessor) |
paul@1007 | 426 | |
paul@1007 | 427 | # Assignments to known attributes. |
paul@1007 | 428 | |
paul@1007 | 429 | if self.final_method == "static-assign": |
paul@1007 | 430 | parent, attrname = self.origin.rsplit(".", 1) |
paul@1021 | 431 | store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" |
paul@1009 | 432 | emit(("<set_attr_ref>", ("__get_object_attr_ref", parent, attrname))) |
paul@1021 | 433 | emit((store, "<attr_ref>", "<assexpr>")) |
paul@1007 | 434 | |
paul@1007 | 435 | # Invoked attributes employ a separate context. |
paul@1007 | 436 | |
paul@1007 | 437 | elif self.final_method in ("static", "static-invoke"): |
paul@1007 | 438 | accessor = ("__load_static_ignore", self.origin) |
paul@1007 | 439 | |
paul@1007 | 440 | # Wrap accesses in context operations. |
paul@1007 | 441 | |
paul@1007 | 442 | if self.context_test == "test": |
paul@1007 | 443 | |
paul@1007 | 444 | # Test and combine the context with static attribute details. |
paul@1007 | 445 | |
paul@1007 | 446 | if self.final_method == "static": |
paul@1007 | 447 | emit(("__load_static_test", context, self.origin)) |
paul@1007 | 448 | |
paul@1007 | 449 | # Test the context, storing it separately if required for the |
paul@1007 | 450 | # immediately invoked static attribute. |
paul@1007 | 451 | |
paul@1007 | 452 | elif self.final_method == "static-invoke": |
paul@1007 | 453 | emit(("<test_context_static>", context, self.origin)) |
paul@1007 | 454 | |
paul@1007 | 455 | # Test the context, storing it separately if required for an |
paul@1007 | 456 | # immediately invoked attribute. |
paul@1007 | 457 | |
paul@1007 | 458 | elif self.final_method == "access-invoke": |
paul@1007 | 459 | emit(("<test_context_revert>", context, accessor)) |
paul@1007 | 460 | |
paul@1007 | 461 | # Test the context and update the attribute details if |
paul@1007 | 462 | # appropriate. |
paul@1007 | 463 | |
paul@1007 | 464 | else: |
paul@1007 | 465 | emit(("__test_context", context, accessor)) |
paul@1007 | 466 | |
paul@1007 | 467 | elif self.context_test == "replace": |
paul@1007 | 468 | |
paul@1007 | 469 | # Produce an object with updated context. |
paul@1007 | 470 | |
paul@1007 | 471 | if self.final_method == "static": |
paul@1007 | 472 | emit(("__load_static_replace", context, self.origin)) |
paul@1007 | 473 | |
paul@1007 | 474 | # Omit the context update operation where the target is static |
paul@1007 | 475 | # and the context is recorded separately. |
paul@1007 | 476 | |
paul@1007 | 477 | elif self.final_method == "static-invoke": |
paul@1007 | 478 | pass |
paul@1007 | 479 | |
paul@1007 | 480 | # If a separate context is used for an immediate invocation, |
paul@1007 | 481 | # produce the attribute details unchanged. |
paul@1007 | 482 | |
paul@1007 | 483 | elif self.final_method == "access-invoke": |
paul@1007 | 484 | emit(accessor) |
paul@1007 | 485 | |
paul@1007 | 486 | # Update the context in the attribute details. |
paul@1007 | 487 | |
paul@1007 | 488 | else: |
paul@1007 | 489 | emit(("__update_context", context, accessor)) |
paul@1007 | 490 | |
paul@1007 | 491 | # Omit the accessor for assignments and for invocations of static |
paul@1007 | 492 | # targets. Otherwise, emit the accessor which may involve the |
paul@1007 | 493 | # invocation of a test. |
paul@1007 | 494 | |
paul@1007 | 495 | elif self.final_method not in ("assign", "static-assign", "static-invoke"): |
paul@1007 | 496 | emit(accessor) |
paul@1007 | 497 | |
paul@1007 | 498 | return accessor |
paul@1007 | 499 | |
paul@1007 | 500 | def process_context_identity(self, context, emit): |
paul@1007 | 501 | |
paul@1007 | 502 | """ |
paul@1007 | 503 | Using 'context', use 'emit' to generate instructions to test the context |
paul@1007 | 504 | identity. |
paul@1007 | 505 | """ |
paul@1007 | 506 | |
paul@1007 | 507 | if context: |
paul@1007 | 508 | |
paul@1007 | 509 | # Only verify the context for invocation purposes if a suitable |
paul@1007 | 510 | # test has been performed. |
paul@1007 | 511 | |
paul@1007 | 512 | if self.context_test in ("ignore", "replace") or \ |
paul@1007 | 513 | self.final_method in ("access-invoke", "static-invoke"): |
paul@1007 | 514 | |
paul@1007 | 515 | emit(("<context_identity_verified>", context)) |
paul@1007 | 516 | else: |
paul@1007 | 517 | emit(("<context_identity>", context)) |
paul@1007 | 518 | |
paul@1007 | 519 | def write(self, f, location): |
paul@1007 | 520 | |
paul@1007 | 521 | "Write the plan to file 'f' with the given 'location' information." |
paul@1007 | 522 | |
paul@1007 | 523 | print >>f, encode_access_location(location), \ |
paul@1007 | 524 | self.name or "{}", \ |
paul@1007 | 525 | self.test and "-".join(self.test) or "{}", \ |
paul@1007 | 526 | self.test_type or "{}", \ |
paul@1007 | 527 | self.base or "{}", \ |
paul@1007 | 528 | ".".join(self.traversed_attrnames) or "{}", \ |
paul@1007 | 529 | ".".join(self.traversed_attrname_modes) or "{}", \ |
paul@1007 | 530 | ".".join(self.remaining_attrnames) or "{}", \ |
paul@1007 | 531 | self.context, self.context_test, \ |
paul@1007 | 532 | self.first_method, self.final_method, self.origin or "{}", \ |
paul@1007 | 533 | ",".join(self.accessor_kinds) |
paul@1007 | 534 | |
paul@1007 | 535 | # vim: tabstop=4 expandtab shiftwidth=4 |