1.1 --- a/micropython/trans.py Mon Jun 01 02:41:57 2009 +0200
1.2 +++ b/micropython/trans.py Mon Jun 01 21:10:47 2009 +0200
1.3 @@ -489,9 +489,9 @@
1.4 need to support the same things as the more general invocations.
1.5 """
1.6
1.7 - target, context, temp = self._generateCallFuncContext()
1.8 - self._generateCallFuncArgs(target, context, temp, args, node)
1.9 - return temp, target
1.10 + target, context, temp_target, temp_context = self._generateCallFuncContext()
1.11 + self._generateCallFuncArgs(target, context, temp_target, temp_context, args, node)
1.12 + return temp_target, target, temp_context
1.13
1.14 def _generateCallFuncContext(self):
1.15
1.16 @@ -511,33 +511,41 @@
1.17 target, context = None, None
1.18
1.19 # Store the target in temporary storage for subsequent referencing.
1.20 - # NOTE: This may not be appropriate for class invocations
1.21 - # NOTE: (instantiation).
1.22
1.23 - temp = self.optimiser.optimise_temp_storage()
1.24 + temp_target = self.optimiser.optimise_temp_storage()
1.25
1.26 # Where a target or context are not known or where an instance is known
1.27 # to be the context, load the context.
1.28
1.29 if target is None or isinstance(context, Instance):
1.30 - self.new_op(temp)
1.31 + self.new_op(temp_target)
1.32 self.new_op(LoadContext())
1.33 + temp_context = self.optimiser.optimise_temp_storage()
1.34 self.new_op(StoreFrame(0))
1.35
1.36 + # Class contexts should be made available for testing of the first
1.37 + # argument.
1.38 + # NOTE: Class methods should eventually be supported.
1.39 +
1.40 + elif isinstance(context, Class):
1.41 + self.new_op(temp_target)
1.42 + self.new_op(LoadContext())
1.43 + temp_context = self.optimiser.optimise_temp_storage()
1.44 +
1.45 # Otherwise omit the context.
1.46
1.47 else:
1.48 - pass # NOTE: Class methods should be supported.
1.49 + temp_context = None
1.50
1.51 - return target, context, temp
1.52 + return target, context, temp_target, temp_context
1.53
1.54 - def _generateCallFuncArgs(self, target, context, temp, args, node):
1.55 + def _generateCallFuncArgs(self, target, context, temp_target, temp_context, args, node):
1.56
1.57 """
1.58 - Given invocation 'target' and 'context' information, the 'temp'
1.59 - reference to the target, a list of nodes representing the 'args'
1.60 - (arguments), generate instructions which load the arguments for the
1.61 - invocation defined by the given 'node'.
1.62 + Given invocation 'target' and 'context' information, the 'temp_target'
1.63 + reference to the target, the 'temp_context' reference to the context, a
1.64 + list of nodes representing the 'args' (arguments), generate instructions
1.65 + which load the arguments for the invocation defined by the given 'node'.
1.66 """
1.67
1.68 # Evaluate the arguments.
1.69 @@ -545,165 +553,173 @@
1.70 employed_positions = set()
1.71 employed_keywords = set()
1.72 extra_keywords = []
1.73 + positional_args = []
1.74 + keyword_args = []
1.75
1.76 # Find keyword arguments in advance in order to help resolve targets.
1.77
1.78 + have_keywords = 0
1.79 +
1.80 for arg in args:
1.81 if isinstance(arg, compiler.ast.Keyword):
1.82 employed_keywords.add(arg.name)
1.83 + keyword_args.append(arg)
1.84 + have_keywords = 1
1.85 + elif not have_keywords:
1.86 + positional_args.append(arg)
1.87
1.88 possible_targets = self.paramtable.all_possible_objects(employed_keywords)
1.89
1.90 # Note the presence of the context in the frame where appropriate.
1.91
1.92 + # For unknown invocations and method invocations.
1.93 +
1.94 if target is None or isinstance(context, Instance):
1.95 ncontext = 1
1.96 - expect_context = 0
1.97 + expect_testable_self = 0
1.98
1.99 - # Handle calls to classes.
1.100 - # The resulting target must match that used in the actual invocation.
1.101 + # Handle calls to classes by obtaining the instantiator function.
1.102 # A context is reserved for the new instance, but this is not provided
1.103 # in the invocation (since the instantiator will fill the locals slot
1.104 # concerned).
1.105
1.106 elif isinstance(target, Class):
1.107 ncontext = 1
1.108 - expect_context = 0
1.109 + expect_testable_self = 0
1.110 target = target.get_instantiator()
1.111
1.112 # Method calls via classes.
1.113
1.114 elif isinstance(context, Class):
1.115 ncontext = 0
1.116 - expect_context = 1
1.117 + expect_testable_self = 1
1.118
1.119 # Function calls.
1.120
1.121 else:
1.122 ncontext = 0
1.123 - expect_context = 0
1.124 + expect_testable_self = 0
1.125 +
1.126 + # Traverse the positional arguments adding them using the incrementing
1.127 + # frame position.
1.128
1.129 first = 1
1.130 frame_pos = ncontext
1.131 - max_keyword_pos = -1
1.132 -
1.133 - for arg in args:
1.134 -
1.135 - # Handle positional and keyword arguments separately.
1.136 -
1.137 - if isinstance(arg, compiler.ast.Keyword):
1.138 -
1.139 - # Optimise where the target is known now.
1.140 -
1.141 - if target is not None:
1.142 -
1.143 - # Find the parameter table entry for the target.
1.144 -
1.145 - target_name = target.full_name()
1.146 -
1.147 - # Look for a callable with the precise target name.
1.148 -
1.149 - table_entry = self.paramtable.table[target_name]
1.150 -
1.151 - # Look the name up in the parameter table entry.
1.152 -
1.153 - try:
1.154 - pos = table_entry[arg.name]
1.155 -
1.156 - # Where no position is found, this could be an extra keyword
1.157 - # argument.
1.158 -
1.159 - except KeyError:
1.160 - extra_keywords.append(arg)
1.161 - continue
1.162 -
1.163 - # Test for illegal conditions.
1.164 -
1.165 - if pos in employed_positions:
1.166 - raise TranslateError(self.module.full_name(), node,
1.167 - "Keyword argument %r overwrites parameter %r." % (arg.name, pos))
1.168 -
1.169 - employed_positions.add(pos)
1.170 -
1.171 - # Generate code for the keyword and the positioning
1.172 - # operation.
1.173 + temp_first_argument = None
1.174
1.175 - self.dispatch(arg.expr)
1.176 - self.new_op(StoreFrame(pos))
1.177 -
1.178 - # Otherwise, generate the code needed to obtain the details of
1.179 - # the parameter location.
1.180 -
1.181 - else:
1.182 -
1.183 - # Combine the target details with the name to get the location.
1.184 - # See the access method on the List class.
1.185 -
1.186 - try:
1.187 - paramindex = self.paramtable.get_index(arg.name)
1.188 -
1.189 - # Where no position is found, this could be an extra keyword
1.190 - # argument.
1.191 -
1.192 - except self.paramtable.TableError:
1.193 - extra_keywords.append(arg)
1.194 - continue
1.195 -
1.196 - # Generate code for the keyword and the positioning
1.197 - # operation. Get the value as the source of the assignment.
1.198 -
1.199 - self.dispatch(arg.expr)
1.200 - self.record_value()
1.201 -
1.202 - # Store the source value using the callable's parameter
1.203 - # table information.
1.204 -
1.205 - self.new_op(temp)
1.206 - self.new_op(StoreFrameIndex(paramindex))
1.207 -
1.208 - self.set_source()
1.209 - self.discard_value()
1.210 -
1.211 - # Record the highest possible frame position for this argument.
1.212 -
1.213 - max_keyword_pos = max(max_keyword_pos, max(self.paramtable.all_attribute_positions(arg.name)))
1.214 -
1.215 - else:
1.216 - self.dispatch(arg)
1.217 - self.new_op(StoreFrame(frame_pos))
1.218 -
1.219 - employed_positions.add(frame_pos)
1.220 + for arg in positional_args:
1.221 + self.dispatch(arg)
1.222 + self.new_op(StoreFrame(frame_pos))
1.223 + employed_positions.add(frame_pos)
1.224
1.225 # Check to see if the first argument is appropriate (compatible with
1.226 # the target where methods are being invoked via classes).
1.227
1.228 - if first and expect_context:
1.229 + if first and (expect_testable_self or target is None):
1.230
1.231 # Drop any test if the target and the context are known.
1.232
1.233 if not self.optimiser.have_correct_self_for_target(context, self.unit):
1.234
1.235 - continue_block = self.new_block()
1.236 -
1.237 - self.new_op(CheckSelf())
1.238 - self.optimiser.set_source(temp)
1.239 - self.new_op(JumpIfTrue(continue_block))
1.240 -
1.241 - # Where the context is inappropriate, drop the incomplete frame and
1.242 - # raise an exception.
1.243 + # Otherwise, remember the first argument for a subsequent
1.244 + # test.
1.245
1.246 - self.new_op(DropFrame())
1.247 - self.new_op(LoadResult())
1.248 -
1.249 - self.make_exception("TypeError", node)
1.250 - self.new_op(StoreException())
1.251 - self.new_op(RaiseException())
1.252 -
1.253 - self.set_block(continue_block)
1.254 + temp_first_argument = self.optimiser.optimise_temp_storage()
1.255
1.256 first = 0
1.257 frame_pos += 1
1.258
1.259 + # Adjust the invocation frame for unknown invocations.
1.260 + # Test the first argument if appropriate.
1.261 +
1.262 + self._generateCallFuncContextTest(temp_target, target, temp_context, temp_first_argument, node)
1.263 +
1.264 + # Traverse the keyword arguments adding them at the appropriate frame
1.265 + # positions.
1.266 +
1.267 + max_keyword_pos = -1
1.268 +
1.269 + for arg in keyword_args:
1.270 +
1.271 + # Optimise where the target is known now.
1.272 +
1.273 + if target is not None:
1.274 +
1.275 + # Find the parameter table entry for the target.
1.276 +
1.277 + target_name = target.full_name()
1.278 +
1.279 + # Look for a callable with the precise target name.
1.280 +
1.281 + table_entry = self.paramtable.table[target_name]
1.282 +
1.283 + # Look the name up in the parameter table entry.
1.284 +
1.285 + try:
1.286 + pos = table_entry[arg.name]
1.287 +
1.288 + # Where no position is found, this could be an extra keyword
1.289 + # argument.
1.290 +
1.291 + except KeyError:
1.292 + extra_keywords.append(arg)
1.293 + continue
1.294 +
1.295 + # Test for illegal conditions.
1.296 +
1.297 + if pos in employed_positions:
1.298 + raise TranslateError(self.module.full_name(), node,
1.299 + "Keyword argument %r overwrites parameter %r." % (arg.name, pos))
1.300 +
1.301 + employed_positions.add(pos)
1.302 +
1.303 + # Generate code for the keyword and the positioning
1.304 + # operation.
1.305 +
1.306 + self.dispatch(arg.expr)
1.307 + self.new_op(StoreFrame(pos))
1.308 +
1.309 + # Otherwise, generate the code needed to obtain the details of
1.310 + # the parameter location.
1.311 +
1.312 + else:
1.313 +
1.314 + # Combine the target details with the name to get the location.
1.315 + # See the access method on the List class.
1.316 +
1.317 + try:
1.318 + paramindex = self.paramtable.get_index(arg.name)
1.319 +
1.320 + # Where no position is found, this could be an extra keyword
1.321 + # argument.
1.322 +
1.323 + except self.paramtable.TableError:
1.324 + extra_keywords.append(arg)
1.325 + continue
1.326 +
1.327 + # Generate code for the keyword and the positioning
1.328 + # operation. Get the value as the source of the assignment.
1.329 +
1.330 + self.dispatch(arg.expr)
1.331 + self.record_value()
1.332 +
1.333 + # Store the source value using the callable's parameter
1.334 + # table information.
1.335 +
1.336 + self.new_op(temp_target)
1.337 + self.new_op(StoreFrameIndex(paramindex))
1.338 +
1.339 + self.set_source()
1.340 + self.discard_value()
1.341 +
1.342 + # Record the highest possible frame position for this argument.
1.343 +
1.344 + max_keyword_pos = max(max_keyword_pos, max(self.paramtable.all_attribute_positions(arg.name)))
1.345 +
1.346 + # Use the frame position counter as a general argument counter.
1.347 +
1.348 + frame_pos += 1
1.349 +
1.350 # NOTE: Extra keywords are not supported.
1.351 # NOTE: Somehow, the above needs to be combined with * arguments.
1.352
1.353 @@ -753,7 +769,7 @@
1.354
1.355 # Where defaults are involved, put them into the frame.
1.356
1.357 - self._generateCallFuncDefaultArgs(target, temp, nargs_min, nargs_max, employed_positions)
1.358 + self._generateCallFuncDefaultArgs(target, temp_target, nargs_min, nargs_max, employed_positions)
1.359
1.360 # Set the frame size.
1.361
1.362 @@ -765,13 +781,13 @@
1.363 max_pos = max(max(employed_positions or [-1]), max_keyword_pos, frame_pos - 1)
1.364 self._endCallFuncArgs(max_pos + 1)
1.365
1.366 - def _generateCallFuncDefaultArgs(self, target, temp, nargs_min, nargs_max, employed_positions):
1.367 + def _generateCallFuncDefaultArgs(self, target, temp_target, nargs_min, nargs_max, employed_positions):
1.368
1.369 """
1.370 - For the given 'target' and 'temp' reference to the target, generate
1.371 - default arguments for those positions in the range 'nargs_min'...
1.372 - 'nargs_max' which are not present in the 'employed_positions'
1.373 - collection.
1.374 + For the given 'target' and 'temp_target' reference to the target,
1.375 + generate default arguments for those positions in the range
1.376 + 'nargs_min'...'nargs_max' which are not present in the
1.377 + 'employed_positions' collection.
1.378 """
1.379
1.380 # Where a lambda is involved, construct a dynamic object to hold the
1.381 @@ -785,13 +801,77 @@
1.382 for pos in range(nargs_min, nargs_max):
1.383 if pos not in employed_positions:
1.384 if dynamic:
1.385 - self.new_op(temp)
1.386 + self.new_op(temp_target)
1.387 self.new_op(LoadAttr(target.default_attrs[pos - nargs_min]))
1.388 else:
1.389 self.new_op(LoadAddress(target.default_attrs[pos - nargs_min]))
1.390 self.new_op(StoreFrame(pos))
1.391
1.392 - def _doCallFunc(self, instruction, target=None):
1.393 + def _generateCallFuncContextTest(self, temp_target, target, temp_context, temp_first_argument, node):
1.394 +
1.395 + """
1.396 + Generate code to test for 'temp_target', representing the given
1.397 + 'target', the context provided by 'temp_context' against
1.398 + 'temp_first_argument', and to signal an exception (using 'node') if the
1.399 + context is incompatible with the first frame argument.
1.400 +
1.401 + In addition, the invocation frame will be shifted if 'temp_context'
1.402 + indicates a function or a class.
1.403 + """
1.404 +
1.405 + adjust_block = self.new_block()
1.406 + continue_block = self.new_block()
1.407 +
1.408 + # Add some preliminary tests where the target is not known.
1.409 +
1.410 + if target is None:
1.411 +
1.412 + # Skip adjustment and tests if a class is being invoked.
1.413 +
1.414 + self.new_op(temp_target)
1.415 + self.new_op(CheckClass())
1.416 + self.new_op(JumpIfTrue(continue_block))
1.417 +
1.418 + # Adjust the frame is no usable context is provided.
1.419 +
1.420 + self.new_op(temp_context)
1.421 + self.new_op(CheckContext())
1.422 + self.new_op(JumpIfFalse(adjust_block))
1.423 +
1.424 + # Skip adjustment and tests if the context is not a class.
1.425 +
1.426 + self.new_op(temp_context)
1.427 + self.new_op(CheckClass())
1.428 + self.new_op(JumpIfFalse(continue_block))
1.429 +
1.430 + if temp_first_argument is not None:
1.431 + self.new_op(temp_first_argument)
1.432 +
1.433 + # Check the current value (the argument) against the known context
1.434 + # (given as the source).
1.435 +
1.436 + self.new_op(CheckSelf())
1.437 + self.optimiser.set_source(temp_context)
1.438 +
1.439 + self.new_op(JumpIfTrue(adjust_block))
1.440 +
1.441 + # Where the context is inappropriate, drop the incomplete frame and
1.442 + # raise an exception.
1.443 +
1.444 + self.new_op(DropFrame())
1.445 + self.new_op(LoadResult())
1.446 +
1.447 + self.make_exception("TypeError", node)
1.448 + self.new_op(StoreException())
1.449 + self.new_op(RaiseException())
1.450 +
1.451 + if target is None or temp_first_argument is not None:
1.452 + self.set_block(adjust_block)
1.453 + self.new_op(AdjustFrame(1))
1.454 +
1.455 + self.set_block(continue_block)
1.456 +
1.457 + def _doCallFunc(self, temp_target, target=None):
1.458
1.459 "Make the invocation."
1.460
1.461 @@ -801,7 +881,7 @@
1.462 if isinstance(target, (Class, Function)):
1.463 self.new_op(JumpWithFrameDirect(target))
1.464 else:
1.465 - self.new_op(instruction)
1.466 + self.new_op(temp_target)
1.467 self.new_op(LoadCallable())
1.468 self.new_op(JumpWithFrame())
1.469
1.470 @@ -812,7 +892,7 @@
1.471 self.frame_makers[-1].attr = nargs
1.472 self.frame_makers.pop()
1.473
1.474 - def _endCallFunc(self, instruction=None, target=None, load_result=1):
1.475 + def _endCallFunc(self, temp_target=None, target=None, temp_context=None, load_result=1):
1.476
1.477 "Finish the invocation and tidy up afterwards."
1.478
1.479 @@ -822,8 +902,11 @@
1.480
1.481 # Discard any temporary storage instructions.
1.482
1.483 - if instruction is not None:
1.484 - self.discard_temp(instruction)
1.485 + if temp_target is not None:
1.486 + self.discard_temp(temp_target)
1.487 +
1.488 + if temp_context is not None:
1.489 + self.discard_temp(temp_context)
1.490
1.491 def _generateFunctionDefaults(self, function):
1.492
1.493 @@ -863,52 +946,6 @@
1.494 else:
1.495 return None
1.496
1.497 - def _generateFunctionContextTest(self, node, check_block):
1.498 -
1.499 - """
1.500 - Generate code to test the context for 'node', jumping to 'check_block'
1.501 - from this code.
1.502 - """
1.503 -
1.504 - adjust_block = self.new_block()
1.505 -
1.506 - # Check the context.
1.507 -
1.508 - temp = LoadName(Attr(0, None, None))
1.509 -
1.510 - # No usable context => remove the context and continue.
1.511 -
1.512 - self.new_op(temp)
1.513 - self.new_op(CheckContext())
1.514 - self.new_op(JumpIfFalse(adjust_block))
1.515 -
1.516 - # Check for a class as the context.
1.517 - # Not a class as context => preserve the context and continue.
1.518 -
1.519 - self.new_op(temp)
1.520 - self.new_op(CheckClassContext())
1.521 - self.new_op(JumpIfFalse(check_block))
1.522 -
1.523 - # Check the context's compatibility with the first parameter.
1.524 - # Compatible class => remove the context and continue.
1.525 - # NOTE: Handle insufficient arguments.
1.526 -
1.527 - self.new_op(LoadName(Attr(1, None, None)))
1.528 - self.new_op(CheckSelf())
1.529 - self.optimiser.set_source(temp)
1.530 - self.new_op(JumpIfTrue(adjust_block))
1.531 -
1.532 - # Incompatible class => type error.
1.533 -
1.534 - self.make_exception("TypeError", node)
1.535 - self.new_op(StoreException())
1.536 - self.new_op(RaiseException())
1.537 -
1.538 - # Remove the context from the parameters.
1.539 -
1.540 - self.set_block(adjust_block)
1.541 - self.new_op(AdjustFrame(1))
1.542 -
1.543 def _visitName(self, node, classes):
1.544
1.545 """