/interpreter/tags/at2-build270707/src/edu/vub/at/objects/natives/NativeATObject.java
Java | 912 lines | 507 code | 113 blank | 292 comment | 64 complexity | 56842bf9cc72c8a36663fde6121a620c MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * NATNil.java created on Jul 13, 2006 at 9:38:51 PM 4 * (c) Programming Technology Lab, 2006 - 2007 5 * Authors: Tom Van Cutsem & Stijn Mostinckx 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following 14 * conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 */ 28package edu.vub.at.objects.natives; 29 30import edu.vub.at.actors.ATActorMirror; 31import edu.vub.at.actors.ATAsyncMessage; 32import edu.vub.at.actors.ATFarReference; 33import edu.vub.at.actors.natives.NATFarReference; 34import edu.vub.at.actors.net.SerializationException; 35import edu.vub.at.eval.Evaluator; 36import edu.vub.at.exceptions.InterpreterException; 37import edu.vub.at.exceptions.XIllegalOperation; 38import edu.vub.at.exceptions.XSelectorNotFound; 39import edu.vub.at.exceptions.XTypeMismatch; 40import edu.vub.at.objects.ATBoolean; 41import edu.vub.at.objects.ATClosure; 42import edu.vub.at.objects.ATContext; 43import edu.vub.at.objects.ATField; 44import edu.vub.at.objects.ATHandler; 45import edu.vub.at.objects.ATMessage; 46import edu.vub.at.objects.ATMethod; 47import edu.vub.at.objects.ATNil; 48import edu.vub.at.objects.ATNumber; 49import edu.vub.at.objects.ATObject; 50import edu.vub.at.objects.ATTable; 51import edu.vub.at.objects.ATTypeTag; 52import edu.vub.at.objects.coercion.NativeTypeTags; 53import edu.vub.at.objects.grammar.ATAssignVariable; 54import edu.vub.at.objects.grammar.ATAssignmentSymbol; 55import edu.vub.at.objects.grammar.ATBegin; 56import edu.vub.at.objects.grammar.ATDefinition; 57import edu.vub.at.objects.grammar.ATExpression; 58import edu.vub.at.objects.grammar.ATMessageCreation; 59import edu.vub.at.objects.grammar.ATSplice; 60import edu.vub.at.objects.grammar.ATStatement; 61import edu.vub.at.objects.grammar.ATSymbol; 62import edu.vub.at.objects.grammar.ATUnquoteSplice; 63import edu.vub.at.objects.mirrors.NATMirage; 64import edu.vub.at.objects.mirrors.NativeClosure; 65import edu.vub.at.objects.mirrors.Reflection; 66import edu.vub.at.objects.symbiosis.JavaClass; 67import edu.vub.at.objects.symbiosis.JavaObject; 68import edu.vub.at.objects.symbiosis.SymbioticATObjectMarker; 69import edu.vub.at.util.logging.Logging; 70 71import java.io.IOException; 72import java.io.InvalidObjectException; 73import java.io.ObjectStreamException; 74import java.io.Serializable; 75 76/** 77 * This class implements default semantics for all test and conversion methods. 78 * It also implements the default metaobject protocol semantics for native 79 * AmbientTalk objects. 80 * <p> 81 * More specifically, this class encapsulates the behavior for: 82 * <ul> 83 * <li>The behavior of all native objects. 84 * <li>The default behavior of all non-native objects. 85 * </ul> 86 * Native AmbientTalk objects contain no fields, only methods. Fields are represented 87 * using accessor methods. Mutable fields also have a mutator method. 88 * <p> 89 * To allow for AmbientTalk language values to be unquoted into parsetrees, 90 * a native object is considered to be a valid ambienttalk expression. 91 * 92 * @author tvcutsem, smostinc 93 */ 94public abstract class NativeATObject implements ATObject, ATExpression, Serializable { 95 96 protected NativeATObject() {}; 97 98 /** 99 * Asynchronous messages ( o<-m( args )) sent in the context of an object o (i.e. 100 * sent in a method or closure where the self pseudovariable is bound to o) are 101 * delegated to the base-level send method of the actor in which the object o is 102 * contained. 103 */ 104 public ATObject meta_send(ATAsyncMessage message) throws InterpreterException { 105 return OBJLexicalRoot._INSTANCE_.base_actor().base_send(message); 106 } 107 108 /** 109 * By default, when an object receives an incoming asynchronous message, it tells 110 * the message to process itself. The message's default behaviour is to subsequently 111 * invoke the method corresponding to the message's selector on this object. 112 */ 113 public ATObject meta_receive(ATAsyncMessage message) throws InterpreterException { 114 return message.base_process(this); 115 } 116 117 /** 118 * An ambienttalk object can respond to a message if a corresponding field or method exists 119 * either in the receiver object locally, or in one of its dynamic parents. 120 * Fields also implicitly define a mutator whose name has the form <tt>field:=</tt>. 121 */ 122 public ATBoolean meta_respondsTo(ATSymbol selector) throws InterpreterException { 123 if (this.hasLocalField(selector) || this.hasLocalMethod(selector)) { 124 return NATBoolean._TRUE_; 125 } else { 126 if (selector.isAssignmentSymbol()) { 127 if (this.hasLocalField(selector.asAssignmentSymbol().getFieldName())) { 128 return NATBoolean._TRUE_; 129 } 130 } 131 } 132 return base_super().meta_respondsTo(selector); 133 } 134 135 /** 136 * By default, when a selection is not understood by a primitive object, an error is raised. 137 */ 138 public ATClosure meta_doesNotUnderstand(ATSymbol selector) throws InterpreterException { 139 throw new XSelectorNotFound(selector, this); 140 } 141 142 /* ------------------------------------------ 143 * -- Slot accessing and mutating protocol -- 144 * ------------------------------------------ */ 145 146 public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException { 147 throw new XIllegalOperation("Cannot add fields to " + Evaluator.valueNameOf(this.getClass())); 148 } 149 150 /* ------------------------------------ 151 * -- Extension and cloning protocol -- 152 * ------------------------------------ */ 153 154 public ATObject meta_clone() throws InterpreterException { 155 throw new XIllegalOperation("Cannot clone a native object of type " + this.getClass().getName()); 156 } 157 158 public ATObject meta_newInstance(ATTable initargs) throws InterpreterException { 159 return Reflection.upInstanceCreation(this, initargs); 160 } 161 162 /* --------------------------------- 163 * -- Structural Access Protocol -- 164 * --------------------------------- */ 165 166 public ATNil meta_addField(ATField field) throws InterpreterException { 167 throw new XIllegalOperation("Cannot add fields to " + Evaluator.valueNameOf(this.getClass())); 168 } 169 170 public ATNil meta_addMethod(ATMethod method) throws InterpreterException { 171 throw new XIllegalOperation("Cannot add methods to " + Evaluator.valueNameOf(this.getClass())); 172 } 173 174 public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException { 175 throw new XSelectorNotFound(fieldName, this); 176 } 177 178 public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException { 179 return Reflection.downBaseLevelMethod(this, methodName); 180 } 181 182 public ATTable meta_listFields() throws InterpreterException { 183 return NATTable.EMPTY; 184 } 185 186 public ATTable meta_listMethods() throws InterpreterException { 187 return NATTable.atValue(Reflection.downBaseLevelMethods(this)); 188 } 189 190 /* --------------------------------- 191 * -- Abstract Grammar Protocol -- 192 * --------------------------------- */ 193 194 /** 195 * All NATObjects which are not Abstract Grammar elements are self-evaluating. 196 */ 197 public ATObject meta_eval(ATContext ctx) throws InterpreterException { 198 return this; 199 } 200 201 /** 202 * Quoting a native object returns itself, except for pure AG elements. 203 */ 204 public ATObject meta_quote(ATContext ctx) throws InterpreterException { 205 return this; 206 } 207 208 public abstract NATText meta_print() throws InterpreterException; 209 210 /* ------------------------------ 211 * -- ATObject Mirror Fields -- 212 * ------------------------------ */ 213 214 /** 215 * Native objects have a SHARES-A parent link to 'nil', by default. 216 */ 217 public ATBoolean meta_isExtensionOfParent() throws InterpreterException { 218 return NATBoolean.atValue((NATObject._SHARES_A_)); 219 }; 220 221 /** 222 * By default numbers, tables and so on have root as their lexical parent. 223 */ 224 public ATObject impl_lexicalParent() throws InterpreterException { 225 return Evaluator.getGlobalLexicalScope(); 226 } 227 228 public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException { 229 return NATBoolean.atValue( 230 this.getClass() == original.getClass()); 231 } 232 233 public ATBoolean meta_isRelatedTo(ATObject object) throws InterpreterException { 234 return this.meta_isCloneOf(object); 235 } 236 237 /* --------------------------------- 238 * -- Type Testing and Querying -- 239 * --------------------------------- */ 240 241 /** 242 * Native objects implement the type test non-recursively: only the type tags 243 * returned by {@link this#meta_getTypeTags()} are tested against. 244 */ 245 public ATBoolean meta_isTaggedAs(ATTypeTag type) throws InterpreterException { 246 ATObject[] types = this.meta_typeTags().asNativeTable().elements_; 247 for (int i = 0; i < types.length; i++) { 248 if (types[i].asTypeTag().base_isSubtypeOf(type).asNativeBoolean().javaValue) { 249 return NATBoolean._TRUE_; 250 } 251 } 252 return NATBoolean._FALSE_; 253 } 254 255 /** 256 * By default, a native object (and also nil) has no type tags. 257 */ 258 public ATTable meta_typeTags() throws InterpreterException { 259 return NATTable.EMPTY; 260 } 261 262 /* ----------------------------- 263 * -- Object Passing protocol -- 264 * ----------------------------- */ 265 266 /** 267 * This method allows objects to decide which object should be serialized in their 268 * stead when they are passed as argument in an asynchronous message send that 269 * crosses actor boundaries. 270 */ 271 public abstract ATObject meta_pass() throws InterpreterException; 272 273 /** 274 * Delegate the responsibility of serialization to the AT/2 meta-level 275 */ 276 public Object writeReplace() throws ObjectStreamException { 277 try { 278 return this.meta_pass(); 279 } catch(InterpreterException e) { 280 throw new InvalidObjectException("Failed to pass object " + this + ": " + e.getMessage()); 281 } 282 } 283 284 public abstract ATObject meta_resolve() throws InterpreterException; 285 286 /** 287 * Delegate the responsibility of deserialization to the AT/2 meta-level 288 */ 289 public Object readResolve() throws ObjectStreamException { 290 try { 291 return this.meta_resolve(); 292 } catch(InterpreterException e) { 293 throw new SerializationException(e); 294 } 295 } 296 297 /* --------------------------------- 298 * -- Value Conversion Protocol -- 299 * --------------------------------- */ 300 301 public boolean isSymbol() throws InterpreterException { 302 return false; 303 } 304 305 public boolean isTable() throws InterpreterException { 306 return false; 307 } 308 309 public boolean isCallFrame() throws InterpreterException { 310 return false; 311 } 312 313 public boolean isUnquoteSplice() throws InterpreterException { 314 return false; 315 } 316 317 public boolean isVariableAssignment() throws InterpreterException { 318 return false; 319 } 320 321 public boolean isSplice() throws InterpreterException { 322 return false; 323 } 324 325 public boolean isMessageCreation() throws InterpreterException { 326 return false; 327 } 328 329 public boolean isAmbientTalkObject() { 330 return false; 331 } 332 333 public boolean isJavaObjectUnderSymbiosis() { 334 return false; 335 } 336 337 public boolean isNativeBoolean() { 338 return false; 339 } 340 341 public boolean isNativeText() { 342 return false; 343 } 344 345 public boolean isNativeField() { 346 return false; 347 } 348 349 public boolean isTypeTag() throws InterpreterException { 350 return false; 351 } 352 353 public ATClosure asClosure() throws InterpreterException { 354 throw new XTypeMismatch(ATClosure.class, this); 355 } 356 357 public ATSymbol asSymbol() throws InterpreterException { 358 throw new XTypeMismatch(ATSymbol.class, this); 359 } 360 361 public ATTable asTable() throws InterpreterException { 362 throw new XTypeMismatch(ATTable.class, this); 363 } 364 365 public ATBoolean asBoolean() throws InterpreterException { 366 throw new XTypeMismatch(ATBoolean.class, this); 367 } 368 369 public ATNumber asNumber() throws InterpreterException { 370 throw new XTypeMismatch(ATNumber.class, this); 371 } 372 373 public ATMessage asMessage() throws InterpreterException { 374 throw new XTypeMismatch(ATMessage.class, this); 375 } 376 377 public ATField asField() throws InterpreterException { 378 throw new XTypeMismatch(ATField.class, this); 379 } 380 381 public ATMethod asMethod() throws InterpreterException { 382 throw new XTypeMismatch(ATMethod.class, this); 383 } 384 385 public ATHandler asHandler() throws InterpreterException { 386 throw new XTypeMismatch(ATHandler.class, this); 387 } 388 389 public ATTypeTag asTypeTag() throws InterpreterException { 390 throw new XTypeMismatch(ATTypeTag.class, this); 391 } 392 393 // Conversions for concurrency and distribution related object 394 public boolean isFarReference() { 395 return false; 396 } 397 398 public ATFarReference asFarReference() throws InterpreterException { 399 throw new XTypeMismatch(ATFarReference.class, this); 400 } 401 402 public ATAsyncMessage asAsyncMessage() throws InterpreterException { 403 throw new XTypeMismatch(ATAsyncMessage.class, this); 404 } 405 406 public ATActorMirror asActorMirror() throws InterpreterException { 407 throw new XTypeMismatch(ATActorMirror.class, this); 408 } 409 410 // Conversions for abstract grammar elements 411 412 public ATStatement asStatement() throws InterpreterException { 413 throw new XTypeMismatch(ATStatement.class, this); 414 } 415 416 public ATDefinition asDefinition() throws InterpreterException { 417 throw new XTypeMismatch(ATDefinition.class, this); 418 } 419 420 public ATExpression asExpression() throws InterpreterException { 421 return this; 422 } 423 424 public ATBegin asBegin() throws InterpreterException { 425 throw new XTypeMismatch(ATBegin.class, this); 426 } 427 428 public ATMessageCreation asMessageCreation() throws InterpreterException { 429 throw new XTypeMismatch(ATMessageCreation.class, this); 430 } 431 432 public ATUnquoteSplice asUnquoteSplice() throws InterpreterException { 433 throw new XTypeMismatch(ATUnquoteSplice.class, this); 434 } 435 436 public ATAssignVariable asVariableAssignment() throws InterpreterException { 437 throw new XTypeMismatch(ATAssignVariable.class, this); 438 } 439 440 public ATSplice asSplice() throws InterpreterException { 441 throw new XTypeMismatch(ATSplice.class, this); 442 } 443 444 // Conversions for native values 445 public NATObject asAmbientTalkObject() throws XTypeMismatch { 446 throw new XTypeMismatch(NATObject.class, this); 447 } 448 449 public NATMirage asMirage() throws XTypeMismatch { 450 throw new XTypeMismatch(NATMirage.class, this); 451 } 452 453 public NATNumber asNativeNumber() throws XTypeMismatch { 454 throw new XTypeMismatch(NATNumber.class, this); 455 } 456 457 public NATFraction asNativeFraction() throws XTypeMismatch { 458 throw new XTypeMismatch(NATFraction.class, this); 459 } 460 461 public NATText asNativeText() throws XTypeMismatch { 462 throw new XTypeMismatch(NATText.class, this); 463 } 464 465 public NATTable asNativeTable() throws XTypeMismatch { 466 throw new XTypeMismatch(NATTable.class, this); 467 } 468 469 public NATBoolean asNativeBoolean() throws XTypeMismatch { 470 throw new XTypeMismatch(NATBoolean.class, this); 471 } 472 473 public NATNumeric asNativeNumeric() throws XTypeMismatch { 474 throw new XTypeMismatch(NATNumeric.class, this); 475 } 476 477 public NATFarReference asNativeFarReference() throws XTypeMismatch { 478 throw new XTypeMismatch(NATFarReference.class, this); 479 } 480 481 public JavaObject asJavaObjectUnderSymbiosis() throws XTypeMismatch { 482 throw new XTypeMismatch(JavaObject.class, this); 483 } 484 485 public JavaClass asJavaClassUnderSymbiosis() throws XTypeMismatch { 486 throw new XTypeMismatch(JavaClass.class, this); 487 } 488 489 /** 490 * Only true objects have a dynamic pointer, native objects denote 'nil' to 491 * be their dynamic parent when asked for it. Note that, for native objects, 492 * 'super' is a read-only field (i.e. only an accessor for the virtual field exists). 493 */ 494 public ATObject base_super() throws InterpreterException { 495 return OBJNil._INSTANCE_; 496 }; 497 498 public String toString() { 499 return Evaluator.toString(this); 500 } 501 502 /** 503 * Java method invocations of equals are transformed into 504 * AmbientTalk '==' method invocations. 505 */ 506 public boolean equals(Object other) { 507 try { 508 if (other instanceof ATObject) { 509 return this.base__opeql__opeql_((ATObject) other).asNativeBoolean().javaValue; 510 } else if (other instanceof SymbioticATObjectMarker) { 511 return this.base__opeql__opeql_( 512 ((SymbioticATObjectMarker) other)._returnNativeAmbientTalkObject()).asNativeBoolean().javaValue; 513 } 514 } catch (InterpreterException e) { 515 Logging.Actor_LOG.warn("Error during equality testing:", e); 516 } 517 return false; 518 } 519 520 /** 521 * By default, two AmbientTalk objects are equal if they are the 522 * same object, or one is a proxy for the same object. 523 */ 524 public ATBoolean base__opeql__opeql_(ATObject other) throws InterpreterException { 525 // by default, ATObjects use pointer equality 526 return NATBoolean.atValue(this == other); 527 } 528 529 public ATObject base_new(ATObject[] initargs) throws InterpreterException { 530 return this.meta_newInstance(NATTable.atValue(initargs)); 531 } 532 533 public ATObject base_init(ATObject[] initargs) throws InterpreterException { 534 return OBJNil._INSTANCE_; 535 } 536 537 /** 538 * This method is used to evaluate code of the form <tt>selector(args)</tt> 539 * or <tt>selector := arg</tt> within the scope of this object. 540 * 541 * It dispatches to other implementation-level methods based on the selector. 542 */ 543 public ATObject impl_call(ATSymbol selector, ATTable arguments) throws InterpreterException { 544 if (selector.isAssignmentSymbol()) { 545 return this.impl_callMutator(selector.asAssignmentSymbol(), arguments); 546 } else { 547 return this.impl_callAccessor(selector, arguments); 548 } 549 } 550 551 /** 552 * Implements the interpretation of <tt>f(arg)</tt> inside the scope of a 553 * particular object. 554 * 555 * - if f is bound to a local method, the method is applied 556 * - if f is bound to a field: 557 * - if the field contains a closure, the closure is applied 558 * - otherwise, the field is treated as a nullary closure and 'applied' 559 * - otherwise, the search for the selector continues in the lexical parent 560 */ 561 public ATObject impl_callAccessor(ATSymbol selector, ATTable arguments) throws InterpreterException { 562 if(this.hasLocalMethod(selector)) { 563 // apply the method with a context ctx where 564 // ctx.scope = the implementing scope, being this object 565 // ctx.self = the receiver, being in this case again the implementor 566 return this.getLocalMethod(selector).base_apply(arguments, new NATContext(this, this)); 567 } else { 568 if (this.hasLocalField(selector)) { 569 ATObject fieldValue = this.getLocalField(selector); 570 571 if (fieldValue.meta_isTaggedAs(NativeTypeTags._CLOSURE_).asNativeBoolean().javaValue) { 572 return fieldValue.asClosure().base_apply(arguments); 573 } else { 574 NativeClosure.checkNullaryArguments(selector, arguments); 575 return fieldValue; 576 } 577 } else { 578 return this.impl_lexicalParent().impl_callAccessor(selector, arguments); 579 } 580 } 581 } 582 583 /** 584 * Implements the interpretation of <tt>x := arg</tt> inside the scope of a 585 * particular object. 586 * 587 * - if x:= is bound to a local method, the method is applied 588 * - if x is bound to a field, the field is assigned if exactly one argument is given 589 * - otherwise, the search for the selector continues in the lexical parent 590 */ 591 public ATObject impl_callMutator(ATAssignmentSymbol selector, ATTable arguments) throws InterpreterException { 592 if(this.hasLocalMethod(selector)) { 593 // apply the method with a context ctx where 594 // ctx.scope = the implementing scope, being this object 595 // ctx.self = the receiver, being in this case again the implementor 596 return this.getLocalMethod(selector).base_apply(arguments, new NATContext(this, this)); 597 } else { 598 ATSymbol fieldSelector = selector.getFieldName(); 599 if (this.hasLocalField(fieldSelector)) { 600 ATObject value = NativeClosure.checkUnaryArguments(selector, arguments); 601 this.setLocalField(fieldSelector, value); 602 return value; 603 } else { 604 return this.impl_lexicalParent().impl_callMutator(selector, arguments); 605 } 606 } 607 } 608 609 /** 610 * Implements the interpretation of <tt>x</tt> inside the scope of a 611 * particular object. 612 * 613 * - if x is bound to a local method, the method is applied to <tt>[]</tt> 614 * - if x is bound to a field, the field's value is returned (even if it 615 * contains a closure) 616 * - otherwise, the search for the selector continues in the lexical parent 617 */ 618 public ATObject impl_callField(ATSymbol selector) throws InterpreterException { 619 // if selector is bound to a method, treat 'm' as 'm()' 620 if(this.hasLocalMethod(selector)) { 621 // apply the method with a context ctx where 622 // ctx.scope = the implementing scope, being this object 623 // ctx.self = the receiver, being in this case again the implementor 624 return this.getLocalMethod(selector).base_apply(NATTable.EMPTY, new NATContext(this, this)); 625 } else { 626 if (this.hasLocalField(selector)) { 627 // simply return the field's value, regardless of whether it is bound to a 628 // closure or not 629 return this.getLocalField(selector); 630 } else { 631 return this.impl_lexicalParent().impl_callField(selector); 632 } 633 } 634 } 635 636 /** 637 * This method dispatches to specific invocation primitives 638 * depending on whether or not the given selector denotes an assignment. 639 */ 640 public ATObject meta_invoke(ATObject receiver, ATSymbol selector, ATTable arguments) throws InterpreterException { 641 // If the selector is an assignment symbol (i.e. `field:=) try to assign the corresponding field 642 if (selector.isAssignmentSymbol()) { 643 return this.impl_invokeMutator(receiver, selector.asAssignmentSymbol(), arguments); 644 } else { 645 return this.impl_invokeAccessor(receiver, selector, arguments); 646 } 647 } 648 649 /** 650 * Implements the interpretation of <tt>o.m(arg)</tt>. 651 * 652 * - if m is bound to a local method of o, the method is applied 653 * - if m is bound to a field of o: 654 * - if the field contains a closure, the closure is applied 655 * - otherwise, the field is treated as a nullary closure and 'applied' 656 * - otherwise, the search for the selector continues in the dynamic parent 657 */ 658 public ATObject impl_invokeAccessor(ATObject receiver, ATSymbol selector, ATTable arguments) throws InterpreterException { 659 if (this.hasLocalMethod(selector)) { 660 // immediately execute the method in the context ctx where 661 // ctx.scope = the implementing scope, being this object, under which an additional callframe will be inserted 662 // ctx.self = the late bound receiver, being the passed receiver 663 return this.getLocalMethod(selector).base_apply(arguments, new NATContext(this, receiver)); 664 } else { 665 if (this.hasLocalField(selector)) { 666 ATObject fieldValue = this.getLocalField(selector); 667 668 if (fieldValue.meta_isTaggedAs(NativeTypeTags._CLOSURE_).asNativeBoolean().javaValue) { 669 return fieldValue.asClosure().base_apply(arguments); 670 } else { 671 NativeClosure.checkNullaryArguments(selector, arguments); 672 return fieldValue; 673 } 674 } else { 675 return base_super().impl_invokeAccessor(receiver, selector, arguments); 676 } 677 } 678 } 679 680 /** 681 * Implements the interpretation of <tt>o.x := arg</tt>. 682 * 683 * - if x:= is bound to a local method of o, the method is applied to the arguments. 684 * - if x is bound to a field of o, the field is treated as a unary mutator method 685 * that assigns the field and 'applied' to the given arguments. 686 * - otherwise, the search for the selector continues in the dynamic parent 687 */ 688 public ATObject impl_invokeMutator(ATObject receiver, ATAssignmentSymbol selector, ATTable arguments) throws InterpreterException { 689 if (this.hasLocalMethod(selector)) { 690 // immediately execute the method in the context ctx where 691 // ctx.scope = the implementing scope, being this object, under which an additional callframe will be inserted 692 // ctx.self = the late bound receiver, being the passed receiver 693 return this.getLocalMethod(selector).base_apply(arguments, new NATContext(this, receiver)); 694 } else { 695 // try to treat a local field as a mutator 696 ATSymbol fieldSelector = selector.getFieldName(); 697 if (this.hasLocalField(fieldSelector)) { 698 ATObject value = NativeClosure.checkUnaryArguments(selector, arguments); 699 this.setLocalField(fieldSelector, value); 700 return value; 701 } else { 702 // if no field matching the selector exists, delegate to the parent 703 return base_super().impl_invokeMutator(receiver, selector, arguments); 704 } 705 } 706 } 707 708 /** 709 * Implements the interpretation of <tt>o.x</tt>. 710 * 711 * - if x is bound to a local method of o, the method is applied to <tt>[]</tt> 712 * - if x is bound to a field of o, the field's value is returned (even if it 713 * contains a closure). 714 * - otherwise, the search for the selector continues in the dynamic parent 715 */ 716 public ATObject meta_invokeField(ATObject receiver, ATSymbol selector) throws InterpreterException { 717 // if selector is bound to a method, treat 'o.m' as 'o.m()' 718 if (this.hasLocalMethod(selector)) { 719 // immediately execute the method in the context ctx where 720 // ctx.scope = the implementing scope, being this object, under which an additional callframe will be inserted 721 // ctx.self = the late bound receiver, being the passed receiver 722 return this.getLocalMethod(selector).base_apply(NATTable.EMPTY, new NATContext(this, receiver)); 723 } else { 724 if (this.hasLocalField(selector)) { 725 // return the field's value, regardless of whether it is a closure or not 726 return this.getLocalField(selector); 727 } else { 728 return base_super().meta_invokeField(receiver, selector); 729 } 730 } 731 } 732 733 /** 734 * This operation dispatches to more specific implementation-level methods depending 735 * on the type of the selector. 736 */ 737 public ATClosure impl_lookup(ATSymbol selector) throws InterpreterException { 738 if (selector.isAssignmentSymbol()) { 739 return this.impl_lookupMutator(selector.asAssignmentSymbol()); 740 } else { 741 return this.impl_lookupAccessor(selector); 742 } 743 } 744 745 /** 746 * Implements the interpretation of <tt>&f</tt> in the scope of this object. 747 * 748 * - if f is bound to a local method, a closure wrapping the method is returned 749 * - if f is bound to a field of o, an accessor closure is returned which yields 750 * the field's value upon application. 751 * - otherwise, the search for the selector continues in the lexical parent 752 */ 753 public ATClosure impl_lookupAccessor(final ATSymbol selector) throws InterpreterException { 754 if (this.hasLocalMethod(selector)) { 755 // return a new closure (mth, ctx) where 756 // mth = the method found in this object 757 // ctx.scope = the implementing scope, being this object 758 // ctx.self = the late bound receiver, being the passed receiver 759 // ctx.super = the parent of the implementor 760 return this.getLocalMethod(selector).base_wrap(this, this); 761 } else { 762 // try to wrap a local field in an accessor 763 if(this.hasLocalField(selector)) { 764 final NativeATObject scope = this; 765 return new NativeClosure.Accessor(selector, this) { 766 public ATObject access() throws InterpreterException { 767 return scope.getLocalField(selector); 768 } 769 }; 770 } else { 771 return this.impl_lexicalParent().impl_lookupAccessor(selector); 772 } 773 } 774 } 775 776 /** 777 * Implements the interpretation of <tt>&f:=</tt> in the scope of this object. 778 * 779 * - if f:= is bound to a local method, a closure wrapping the method is returned 780 * - if f is bound to a field of o, a mutator closure is returned which assigns 781 * the field upon application. 782 * - otherwise, the search for the selector continues in the lexical parent 783 */ 784 public ATClosure impl_lookupMutator(ATAssignmentSymbol selector) throws InterpreterException { 785 if (this.hasLocalMethod(selector)) { 786 // return a new closure (mth, ctx) where 787 // mth = the method found in this object 788 // ctx.scope = the implementing scope, being this object 789 // ctx.self = the late bound receiver, being the passed receiver 790 // ctx.super = the parent of the implementor 791 return this.getLocalMethod(selector).base_wrap(this, this); 792 } else { 793 final ATSymbol fieldSelector = selector.getFieldName(); 794 // try to wrap a local field in a mutator 795 if(this.hasLocalField(fieldSelector)) { 796 final NativeATObject scope = this; 797 return new NativeClosure.Mutator(selector, this) { 798 public ATObject mutate(ATObject arg) throws InterpreterException { 799 scope.setLocalField(fieldSelector, arg); 800 return arg; 801 } 802 }; 803 } else { 804 return this.impl_lexicalParent().impl_lookupMutator(selector); 805 } 806 } 807 } 808 809 /** 810 * select should dispatch to specific selection primitives 811 * depending on whether or not the given selector denotes an assignment. 812 */ 813 public ATClosure meta_select(ATObject receiver, final ATSymbol selector) throws InterpreterException { 814 if (selector.isAssignmentSymbol()) { 815 return this.impl_selectMutator(receiver, selector.asAssignmentSymbol()); 816 } else { 817 return this.impl_selectAccessor(receiver, selector); 818 } 819 } 820 821 /** 822 * Implements the interpretation of <tt>o.&m</tt>. 823 * 824 * - if m is bound to a local method, a closure wrapping the method is returned 825 * - if m is bound to a field of o, an accessor closure is returned which yields 826 * the field's value upon application. 827 * - otherwise, the search for the selector continues in the dynamic parent 828 */ 829 public ATClosure impl_selectAccessor(ATObject receiver, final ATSymbol selector) throws InterpreterException { 830 if (this.hasLocalMethod(selector)) { 831 // return a new closure (mth, ctx) where 832 // mth = the method found in this object 833 // ctx.scope = the implementing scope, being this object 834 // ctx.self = the late bound receiver, being the passed receiver 835 // ctx.super = the parent of the implementor 836 return this.getLocalMethod(selector).base_wrap(this, receiver); 837 } else { 838 if (this.hasLocalField(selector)) { 839 final NativeATObject scope = this; 840 return new NativeClosure.Accessor(selector, this) { 841 public ATObject access() throws InterpreterException { 842 return scope.getLocalField(selector); 843 } 844 }; 845 } else { 846 return base_super().impl_selectAccessor(receiver, selector); 847 } 848 } 849 } 850 851 /** 852 * Implements the interpretation of <tt>o.&m:=</tt>. 853 * 854 * - if m:= is bound to a local method, a closure wrapping the method is returned 855 * - if m is bound to a field of o, a mutator closure is returned which assigns 856 * the field upon application. 857 * - otherwise, the search for the selector continues in the dynamic parent 858 */ 859 public ATClosure impl_selectMutator(ATObject receiver, final ATAssignmentSymbol selector) throws InterpreterException { 860 if (this.hasLocalMethod(selector)) { 861 // return a new closure (mth, ctx) where 862 // mth = the method found in this object 863 // ctx.scope = the implementing scope, being this object 864 // ctx.self = the late bound receiver, being the passed receiver 865 // ctx.super = the parent of the implementor 866 return this.getLocalMethod(selector).base_wrap(this, receiver); 867 } else { 868 // try to wrap a local field in a mutator 869 final ATSymbol fieldSelector = selector.getFieldName(); 870 if (this.hasLocalField(fieldSelector)) { 871 final NativeATObject scope = this; 872 return new NativeClosure.Mutator(selector, this) { 873 public ATObject mutate(ATObject arg) throws InterpreterException { 874 scope.setLocalField(fieldSelector, arg); 875 return arg; 876 } 877 }; 878 } else { 879 // if no field matching the selector exists, delegate to the parent 880 return base_super().impl_selectMutator(receiver, selector); 881 } 882 } 883 } 884 885 /** native objects have no fields */ 886 protected boolean hasLocalField(ATSymbol sym) throws InterpreterException { 887 return false; 888 } 889 890 /** 891 * A native Java object has a local method if it implements a 892 * native Java method corresponding to the selector prefixed by 'base_'. 893 */ 894 protected boolean hasLocalMethod(ATSymbol atSelector) throws InterpreterException { 895 String jSelector = Reflection.upBaseLevelSelector(atSelector); 896 return Reflection.upRespondsTo(this, jSelector); 897 } 898 899 protected ATObject getLocalField(ATSymbol selector) throws InterpreterException { 900 throw new XSelectorNotFound(selector, this); 901 } 902 903 protected ATMethod getLocalMethod(ATSymbol selector) throws InterpreterException { 904 String methSelector = Reflection.upBaseLevelSelector(selector); 905 return Reflection.upMethodSelection(this, methSelector, selector); 906 } 907 908 protected void setLocalField(ATSymbol selector, ATObject value) throws InterpreterException { 909 throw new XSelectorNotFound(selector, this); 910 } 911 912}