Class: RubyBreaker::Runtime::TypeSystem
- Inherits:
-
Object
- Object
- RubyBreaker::Runtime::TypeSystem
- Defined in:
- lib/rubybreaker/runtime/type_system.rb
Overview
This is the default type system for RubyBreaker. It can be overridden by a user specified type system. See pluggable.rb
for how this can be done.
Instance Method Summary collapse
-
#break_after_method(obj, meth_info) ⇒ Object
This method occurs after every call to a “monitored” method call of a module/class specified for “breaking”.
-
#break_before_method(obj, meth_info) ⇒ Object
This method occurs before every call to a “monitored” method in a module/class specified for breaking.
-
#check_after_method(obj, meth_info) ⇒ Object
This method is invoked after the original method is executed.
-
#check_before_method(obj, meth_info) ⇒ Object
This method is invoked before the original method is executed.
Methods included from Pluggable
#break_after_method_call, #break_before_method_call, #check_after_method_call, #check_before_method_call
Instance Method Details
#break_after_method(obj, meth_info) ⇒ Object
This method occurs after every call to a “monitored” method call of a module/class specified for “breaking”. It updates the type information.
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 |
# File 'lib/rubybreaker/runtime/type_system.rb', line 386 def break_after_method(obj, meth_info) is_obj_mod = (obj.class == Class or obj.class == Module) mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class # Take things out of the method info object meth_name = meth_info.meth_name retval = meth_info.ret args = meth_info.args blk = meth_info.blk RubyBreaker.log("break_after_method #{mod}##{meth_name} started") # Compute the least upper bound lub(obj, TYPE_MAP[mod], meth_name, retval, *args, &blk) if obj == retval # It is possible that the method receiver is a wrapped object if # it is an argument to a method in the current call stack. So this # check is to return the wrapped object and not the stripped off # version. (Remember, == is overridden for the wrapped object.) meth_info.ret = obj end RubyBreaker.log("break_after_method #{mod}##{meth_name} ended") end |
#break_before_method(obj, meth_info) ⇒ Object
This method occurs before every call to a “monitored” method in a module/class specified for breaking. It wraps each argument with the object wrapper so it can be tracked of the method calls.
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 |
# File 'lib/rubybreaker/runtime/type_system.rb', line 314 def break_before_method(obj, meth_info) # Use the eigen class if the object is a module/class and use the # object's class otherwise. is_obj_mod = (obj.class == Class or obj.class == Module) mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class # Let's take things out of the MethodInfo object meth_name = meth_info.meth_name args = meth_info.args blk = meth_info.blk ret = meth_info.ret RubyBreaker.log("break_before_method #{mod}##{meth_name} started") args = args.map do |arg| if arg == nil || arg.kind_of?(TrueClass) || arg.kind_of?(FalseClass) # XXX: would overrides resolve this issue? arg elsif arg.respond_to?(WRAPPED_INDICATOR) # Don't need to wrap an object that is already wrapped arg else ObjectWrapper.new(arg) end end # Using this module object, retrieve the method type map which # maps method names to method types. meth_type_map = TYPE_MAP[mod] # Using the method name, get the type of this method from the map. meth_type = meth_type_map[meth_name] if meth_type # This means the method type has been created previously. unless !meth_type.instance_of?(MethodType) || (blk == nil && meth_type.blk_type == nil) && (!blk || blk.arity == meth_type.blk_type.arg_types.length) raise Errors::TypeError.new("Block usage is inconsistent") end else # No method type has been created for this method yet. Create a # blank method type (where each argument type, block type, and # return type are all nil). # # First, use the orignal method's arity to find out # of # arguments. meth_obj = obj.method(Monitor.get_alt_meth_name(meth_name)) arity = meth_obj.arity arg_types = [nil] * meth_obj.arity.abs if blk # Do the same for the block too if there is one blk_arity = blk.arity blk_arg_types = [nil] * blk_artiy.abs blk_type = BlockType.new(blk_arg_types, nil, nil) else blk_type = nil end meth_type = MethodType.new(meth_name, arg_types, blk_type, nil) meth_type_map[meth_name] = meth_type end meth_info.args = args RubyBreaker.log("break_before_method #{mod}##{meth_name} ended") end |
#check_after_method(obj, meth_info) ⇒ Object
This method is invoked after the original method is executed.
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 |
# File 'lib/rubybreaker/runtime/type_system.rb', line 284 def check_after_method(obj, meth_info) is_obj_mod = (obj.class == Class or obj.class == Module) mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class # Get the method type map for the module/class from the global map. meth_type_map = TYPE_MAP[mod] return unless meth_type_map # Let's take things out of the MethodInfo object meth_name = meth_info.meth_name ret = meth_info.ret RubyBreaker.log("check_after_method #{mod}##{meth_name} started") # Get the registered method type for this method meth_type = meth_type_map[meth_name] ret_type = NominalType.new(ret.class) if !meth_type.ret_type.subtype_of?(ret_type) msg = type_error_msg_prefix(mod, meth_name) + " return value does not have type #{ret_type.unparse()}." raise Errors::ReturnTypeError.new(msg) end RubyBreaker.log("check_after_method #{mod}##{meth_name} started") end |
#check_before_method(obj, meth_info) ⇒ Object
This method is invoked before the original method is executed.
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 |
# File 'lib/rubybreaker/runtime/type_system.rb', line 216 def check_before_method(obj, meth_info) is_obj_mod = (obj.class == Class or obj.class == Module) mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class meth_type_map = TYPE_MAP[mod] return unless meth_type_map # Let's take things out of the MethodInfo object meth_name = meth_info.meth_name args = meth_info.args # blk = meth_info.blk RubyBreaker.log("check_before_method #{mod}##{meth_name} started") # Get the registered method type for this method meth_type = meth_type_map[meth_name] # Do an arity check first. if !arity_check(args.size, meth_type) msg = type_error_msg_prefix(mod, meth_name) + " has an arity of #{meth_type.arg_types.size} " + "but #{args.size} arguments were passed in" raise Errors::ArityError.new(msg) end # Remember what the last formal argument type was so that, if it is # a variable length argument type, we use to check the remaining # arguments. last_supertype = nil # Check actual arguments up until the last position of the formal # argument type. If the number of the formal arguments is less than # actual arguments, it means the last formal argument is a variable # length. If it's the other way around, there are optional # arguments. meth_type.arg_types.each_with_index do |supertype, i| if supertype.kind_of?(OptionalType) || supertype.kind_of?(VarLengthType) supertype = supertype.type end last_supertype = supertype break if i >= args.size subtype = NominalType.new(args[i].class) if !subtype.subtype_of?(supertype) msg = type_error_msg_prefix(mod, meth_name) + "'s #{Util.ordinalize(i+1)} argument " + "does not have type #{supertype.unparse()}." raise Errors::ArgumentTypeError.new(msg) end end # Handle the remaining actual arguments if meth_type.arg_types.size < args.size for i in meth_type.arg_types.size..args.size-1 subtype = NominalType.new(args[i].class) if !subtype.subtype_of?(last_supertype) msg = type_error_msg_prefix(mod, meth_name) + "'s #{Util.ordinalize(i+1)} argument " + "does not have type #{last_supertype.unparse()}." raise Errors::ArgumentTypeError.new(msg) end end end RubyBreaker.log("check_before_method #{mod}##{meth_name} ended") end |