1 /*
   2  * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 /*
  26  * Licensed Materials - Property of IBM
  27  * RMI-IIOP v1.0
  28  * Copyright IBM Corp. 1998 1999  All Rights Reserved
  29  *
  30  */
  31 
  32 package sun.rmi.rmic.iiop;
  33 
  34 import java.util.Vector;
  35 import sun.tools.java.ClassNotFound;
  36 import sun.tools.java.ClassDeclaration;
  37 import sun.tools.java.ClassDefinition;
  38 import sun.tools.java.MemberDefinition;
  39 import java.util.Hashtable;
  40 import java.io.ObjectStreamClass;
  41 import java.io.ObjectStreamField;
  42 
  43 
  44 /**
  45  * ValueType represents any non-special class which does inherit from
  46  * java.io.Serializable and does not inherit from java.rmi.Remote.
  47  * <p>
  48  * The static forValue(...) method must be used to obtain an instance, and
  49  * will return null if the ClassDefinition is non-conforming.
  50  *
  51  * @author      Bryan Atsatt
  52  */
  53 public class ValueType extends ClassType {
  54 
  55     private boolean isCustom;
  56 
  57     //_____________________________________________________________________
  58     // Public Interfaces
  59     //_____________________________________________________________________
  60 
  61     /**
  62      * Create an ValueType object for the given class.
  63      *
  64      * If the class is not a properly formed or if some other error occurs, the
  65      * return value will be null, and errors will have been reported to the
  66      * supplied BatchEnvironment.
  67      */
  68     public static ValueType forValue(ClassDefinition classDef,
  69                                      ContextStack stack,
  70                                      boolean quiet) {
  71 
  72         if (stack.anyErrors()) return null;
  73 
  74         // Do we already have it?
  75 
  76         sun.tools.java.Type theType = classDef.getType();
  77         String typeKey = theType.toString();
  78         Type existing = getType(typeKey,stack);
  79 
  80         if (existing != null) {
  81 
  82             if (!(existing instanceof ValueType)) return null; // False hit.
  83 
  84             // Yep, so return it...
  85 
  86             return (ValueType) existing;
  87         }
  88 
  89         // Is this java.lang.Class?
  90 
  91         boolean javaLangClass = false;
  92 
  93         if (classDef.getClassDeclaration().getName() == idJavaLangClass) {
  94 
  95             // Yes, so replace classDef with one for
  96             // javax.rmi.CORBA.ClassDesc...
  97 
  98             javaLangClass = true;
  99             BatchEnvironment env = stack.getEnv();
 100             ClassDeclaration decl = env.getClassDeclaration(idClassDesc);
 101             ClassDefinition def = null;
 102 
 103             try {
 104                 def = decl.getClassDefinition(env);
 105             } catch (ClassNotFound ex) {
 106                 classNotFound(stack,ex);
 107                 return null;
 108             }
 109 
 110             classDef = def;
 111         }
 112 
 113         // Could this be a value?
 114 
 115         if (couldBeValue(stack,classDef)) {
 116 
 117             // Yes, so check it...
 118 
 119             ValueType it = new ValueType(classDef,stack,javaLangClass);
 120             putType(typeKey,it,stack);
 121             stack.push(it);
 122 
 123             if (it.initialize(stack,quiet)) {
 124                 stack.pop(true);
 125                 return it;
 126             } else {
 127                 removeType(typeKey,stack);
 128                 stack.pop(false);
 129                 return null;
 130             }
 131         } else {
 132             return null;
 133         }
 134     }
 135 
 136 
 137     /**
 138      * Return a string describing this type.
 139      */
 140     public String getTypeDescription () {
 141         String result = addExceptionDescription("Value");
 142         if (isCustom) {
 143             result = "Custom " + result;
 144         }
 145         if (isIDLEntity) {
 146             result = result + " [IDLEntity]";
 147         }
 148         return result;
 149     }
 150 
 151     /**
 152      * Return true if this type is a "custom" type (i.e.
 153      * it implements java.io.Externalizable or has a
 154      * method with the following signature:
 155      *
 156      *  private void writeObject(java.io.ObjectOutputStream out);
 157      *
 158      */
 159     public boolean isCustom () {
 160         return isCustom;
 161     }
 162 
 163 
 164     //_____________________________________________________________________
 165     // Subclass/Internal Interfaces
 166     //_____________________________________________________________________
 167 
 168     /**
 169      * Create a ValueType instance for the given class.  The resulting
 170      * object is not yet completely initialized.
 171      */
 172     private ValueType(ClassDefinition classDef,
 173                       ContextStack stack,
 174                       boolean isMappedJavaLangClass) {
 175         super(stack,classDef,TYPE_VALUE | TM_CLASS | TM_COMPOUND);
 176         isCustom = false;
 177 
 178         // If this is the mapped version of java.lang.Class,
 179         // set the non-IDL names back to java.lang.Class...
 180 
 181         if (isMappedJavaLangClass) {
 182             setNames(idJavaLangClass,IDL_CLASS_MODULE,IDL_CLASS);
 183         }
 184     }
 185 
 186     //_____________________________________________________________________
 187     // Internal Interfaces
 188     //_____________________________________________________________________
 189 
 190     /**
 191      * Initialize this instance.
 192      */
 193 
 194     private static boolean couldBeValue(ContextStack stack, ClassDefinition classDef) {
 195 
 196         boolean result = false;
 197         ClassDeclaration classDecl = classDef.getClassDeclaration();
 198         BatchEnvironment env = stack.getEnv();
 199 
 200         try {
 201             // Make sure it's not remote...
 202 
 203             if (env.defRemote.implementedBy(env, classDecl)) {
 204                 failedConstraint(10,false,stack,classDef.getName());
 205             } else {
 206 
 207                 // Make sure it's Serializable...
 208 
 209                 if (!env.defSerializable.implementedBy(env, classDecl)) {
 210                     failedConstraint(11,false,stack,classDef.getName());
 211                 } else {
 212                     result = true;
 213                 }
 214             }
 215         } catch (ClassNotFound e) {
 216             classNotFound(stack,e);
 217         }
 218 
 219         return result;
 220     }
 221 
 222     /**
 223      * Initialize this instance.
 224      */
 225     private boolean initialize (ContextStack stack, boolean quiet) {
 226 
 227         ClassDefinition ourDef = getClassDefinition();
 228         ClassDeclaration ourDecl = getClassDeclaration();
 229 
 230         try {
 231 
 232             // Make sure our parentage is ok...
 233 
 234             if (!initParents(stack)) {
 235                 failedConstraint(12,quiet,stack,getQualifiedName());
 236                 return false;
 237             }
 238 
 239 
 240             // We're ok, so make up our collections...
 241 
 242             Vector directInterfaces = new Vector();
 243             Vector directMethods = new Vector();
 244             Vector directMembers = new Vector();
 245 
 246             // Get interfaces...
 247 
 248             if (addNonRemoteInterfaces(directInterfaces,stack) != null) {
 249 
 250                 // Get methods...
 251 
 252                 if (addAllMethods(ourDef,directMethods,false,false,stack) != null) {
 253 
 254                     // Update parent class methods
 255                     if (updateParentClassMethods(ourDef,directMethods,false,stack) != null) {
 256 
 257                     // Get constants and members...
 258 
 259                     if (addAllMembers(directMembers,false,false,stack)) {
 260 
 261                         // We're ok, so pass 'em up...
 262 
 263                         if (!initialize(directInterfaces,directMethods,directMembers,stack,quiet)) {
 264                             return false;
 265                         }
 266 
 267                         // Is this class Externalizable?
 268 
 269                         boolean externalizable = false;
 270                         if (!env.defExternalizable.implementedBy(env, ourDecl)) {
 271 
 272                             // No, so check to see if we have a serialPersistentField
 273                             // that will modify the members.
 274 
 275                             if (!checkPersistentFields(getClassInstance(),quiet)) {
 276                                 return false;
 277                             }
 278                         } else {
 279 
 280                             // Yes.
 281 
 282                             externalizable = true;
 283                         }
 284 
 285                         // Should this class be considered "custom"? It is if
 286                         // it is Externalizable OR if it has a method with the
 287                         // following signature:
 288                         //
 289                         //  private void writeObject(java.io.ObjectOutputStream out);
 290                         //
 291 
 292                         if (externalizable) {
 293                             isCustom = true;
 294                         } else {
 295                             for (MemberDefinition member = ourDef.getFirstMember();
 296                                  member != null;
 297                                  member = member.getNextMember()) {
 298 
 299                                 if (member.isMethod() &&
 300                                     !member.isInitializer() &&
 301                                     member.isPrivate() &&
 302                                     member.getName().toString().equals("writeObject")) {
 303 
 304                                     // Check return type, arguments and exceptions...
 305 
 306                                     sun.tools.java.Type methodType = member.getType();
 307                                     sun.tools.java.Type rtnType = methodType.getReturnType();
 308 
 309                                     if (rtnType == sun.tools.java.Type.tVoid) {
 310 
 311                                         // Return type is correct. How about arguments?
 312 
 313                                         sun.tools.java.Type[] args = methodType.getArgumentTypes();
 314                                         if (args.length == 1 &&
 315                                             args[0].getTypeSignature().equals("Ljava/io/ObjectOutputStream;")) {
 316 
 317                                             // Arguments are correct, so it is a custom
 318                                             // value type...
 319 
 320                                             isCustom = true;
 321                                         }
 322                                     }
 323                                 }
 324                             }
 325                         }
 326                         }
 327 
 328                         return true;
 329                     }
 330                 }
 331             }
 332         } catch (ClassNotFound e) {
 333             classNotFound(stack,e);
 334         }
 335 
 336         return false;
 337     }
 338 
 339 
 340     private boolean checkPersistentFields (Class clz, boolean quiet) {
 341 
 342         // Do we have a writeObject method?
 343 
 344         for (int i = 0; i < methods.length; i++) {
 345             if (methods[i].getName().equals("writeObject") &&
 346                 methods[i].getArguments().length == 1) {
 347 
 348                 Type returnType = methods[i].getReturnType();
 349                 Type arg = methods[i].getArguments()[0];
 350                 String id = arg.getQualifiedName();
 351 
 352                 if (returnType.isType(TYPE_VOID) &&
 353                     id.equals("java.io.ObjectOutputStream")) {
 354 
 355                     // Got one, so there's nothing to do...
 356 
 357                     return true;
 358                 }
 359             }
 360         }
 361 
 362         // Do we have a valid serialPersistentField array?
 363 
 364         MemberDefinition spfDef = null;
 365 
 366         for (int i = 0; i < members.length; i++) {
 367             if (members[i].getName().equals("serialPersistentFields")) {
 368 
 369                 Member member = members[i];
 370                 Type type = member.getType();
 371                 Type elementType = type.getElementType();
 372 
 373                 // We have a member with the correct name. Make sure
 374                 // we have the correct signature...
 375 
 376                 if (elementType != null &&
 377                     elementType.getQualifiedName().equals(
 378                                                           "java.io.ObjectStreamField")
 379                     ) {
 380 
 381                     if (member.isStatic() &&
 382                         member.isFinal() &&
 383                         member.isPrivate()) {
 384 
 385                         // We have the correct signature
 386 
 387                         spfDef = member.getMemberDefinition();
 388 
 389                     } else {
 390 
 391                         // Bad signature...
 392 
 393                         failedConstraint(4,quiet,stack,getQualifiedName());
 394                         return false;
 395                     }
 396                 }
 397             }
 398         }
 399 
 400         // If we do not have a serialPersistentField,
 401         // there's nothing to do, so return with no error...
 402 
 403         if (spfDef == null) {
 404             return true;
 405         }
 406 
 407         // Ok, now we must examine the contents of the array -
 408         // then validate them...
 409 
 410         Hashtable fields = getPersistentFields(clz);
 411         boolean result = true;
 412 
 413         for (int i = 0; i < members.length; i++) {
 414             String fieldName = members[i].getName();
 415             String fieldType = members[i].getType().getSignature();
 416 
 417             // Is this field present in the array?
 418 
 419             String type = (String) fields.get(fieldName);
 420 
 421             if (type == null) {
 422 
 423                 // No, so mark it transient...
 424 
 425                 members[i].setTransient();
 426 
 427             } else {
 428 
 429                 // Yes, does the type match?
 430 
 431                 if (type.equals(fieldType)) {
 432 
 433                     // Yes, so remove it from the fields table...
 434 
 435                     fields.remove(fieldName);
 436 
 437                 } else {
 438 
 439                     // No, so error...
 440 
 441                     result = false;
 442                     failedConstraint(2,quiet,stack,fieldName,getQualifiedName());
 443                 }
 444             }
 445         }
 446 
 447         // Ok, we've checked all of our fields. Are there any left in the "array"?
 448         // If so, it's an error...
 449 
 450         if (result && fields.size() > 0) {
 451 
 452             result = false;
 453             failedConstraint(9,quiet,stack,getQualifiedName());
 454         }
 455 
 456         // Return result...
 457 
 458         return result;
 459     }
 460 
 461     /**
 462      * Get the names and types of all the persistent fields of a Class.
 463      */
 464     private Hashtable getPersistentFields (Class clz) {
 465         Hashtable result = new Hashtable();
 466         ObjectStreamClass osc = ObjectStreamClass.lookup(clz);
 467         if (osc != null) {
 468             ObjectStreamField[] fields = osc.getFields();
 469             for (int i = 0; i < fields.length; i++) {
 470                 String typeSig;
 471                 String typePrefix = String.valueOf(fields[i].getTypeCode());
 472                 if (fields[i].isPrimitive()) {
 473                     typeSig = typePrefix;
 474                 } else {
 475                     if (fields[i].getTypeCode() == '[') {
 476                         typePrefix = "";
 477                     }
 478                     typeSig = typePrefix + fields[i].getType().getName().replace('.','/');
 479                     if (typeSig.endsWith(";")) {
 480                         typeSig = typeSig.substring(0,typeSig.length()-1);
 481                     }
 482                 }
 483                 result.put(fields[i].getName(),typeSig);
 484             }
 485         }
 486         return result;
 487     }
 488 }