1 /* 2 * Copyright (c) 2012, 2013, 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 package java.lang.invoke; 27 28 import jdk.internal.org.objectweb.asm.*; 29 import sun.invoke.util.BytecodeDescriptor; 30 import sun.misc.Unsafe; 31 import sun.security.action.GetPropertyAction; 32 33 import java.io.FilePermission; 34 import java.io.Serializable; 35 import java.lang.reflect.Constructor; 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.util.LinkedHashSet; 39 import java.util.concurrent.atomic.AtomicInteger; 40 import java.util.PropertyPermission; 41 import java.util.Set; 42 43 import static jdk.internal.org.objectweb.asm.Opcodes.*; 44 45 /** 46 * Lambda metafactory implementation which dynamically creates an 47 * inner-class-like class per lambda callsite. 48 * 49 * @see LambdaMetafactory 50 */ 51 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 52 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 53 54 private static final int CLASSFILE_VERSION = 52; 55 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); 56 private static final String JAVA_LANG_OBJECT = "java/lang/Object"; 57 private static final String NAME_CTOR = "<init>"; 58 private static final String NAME_FACTORY = "get$Lambda"; 59 60 //Serialization support 61 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda"; 62 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException"; 63 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; 64 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V"; 65 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V"; 66 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 67 private static final String NAME_METHOD_READ_OBJECT = "readObject"; 68 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; 69 private static final String DESCR_CTOR_SERIALIZED_LAMBDA 70 = MethodType.methodType(void.class, 71 Class.class, 72 String.class, String.class, String.class, 73 int.class, String.class, String.class, String.class, 74 String.class, 75 Object[].class).toMethodDescriptorString(); 76 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION 77 = MethodType.methodType(void.class, String.class).toMethodDescriptorString(); 78 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION}; 79 80 81 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 82 83 // Used to ensure that each spun class name is unique 84 private static final AtomicInteger counter = new AtomicInteger(0); 85 86 // For dumping generated classes to disk, for debugging purposes 87 private static final ProxyClassesDumper dumper; 88 89 static { 90 final String key = "jdk.internal.lambda.dumpProxyClasses"; 91 String path = AccessController.doPrivileged( 92 new GetPropertyAction(key), null, 93 new PropertyPermission(key , "read")); 94 dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path); 95 } 96 97 // See context values in AbstractValidatingLambdaMetafactory 98 private final String implMethodClassName; // Name of type containing implementation "CC" 99 private final String implMethodName; // Name of implementation method "impl" 100 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 101 private final Class<?> implMethodReturnClass; // class for implementation method return type "Ljava/lang/String;" 102 private final MethodType constructorType; // Generated class constructor type "(CC)void" 103 private final ClassWriter cw; // ASM class writer 104 private final String[] argNames; // Generated names for the constructor arguments 105 private final String[] argDescs; // Type descriptors for the constructor arguments 106 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" 107 108 /** 109 * General meta-factory constructor, supporting both standard cases and 110 * allowing for uncommon options such as serialization or bridging. 111 * 112 * @param caller Stacked automatically by VM; represents a lookup context 113 * with the accessibility privileges of the caller. 114 * @param invokedType Stacked automatically by VM; the signature of the 115 * invoked method, which includes the expected static 116 * type of the returned lambda object, and the static 117 * types of the captured arguments for the lambda. In 118 * the event that the implementation method is an 119 * instance method, the first argument in the invocation 120 * signature will correspond to the receiver. 121 * @param samMethodName Name of the method in the functional interface to 122 * which the lambda or method reference is being 123 * converted, represented as a String. 124 * @param samMethodType Type of the method in the functional interface to 125 * which the lambda or method reference is being 126 * converted, represented as a MethodType. 127 * @param implMethod The implementation method which should be called (with 128 * suitable adaptation of argument types, return types, 129 * and adjustment for captured arguments) when methods of 130 * the resulting functional interface instance are invoked. 131 * @param instantiatedMethodType The signature of the primary functional 132 * interface method after type variables are 133 * substituted with their instantiation from 134 * the capture site 135 * @param isSerializable Should the lambda be made serializable? If set, 136 * either the target type or one of the additional SAM 137 * types must extend {@code Serializable}. 138 * @param markerInterfaces Additional interfaces which the lambda object 139 * should implement. 140 * @param additionalBridges Method types for additional signatures to be 141 * bridged to the implementation method 142 * @throws LambdaConversionException If any of the meta-factory protocol 143 * invariants are violated 144 */ 145 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 146 MethodType invokedType, 147 String samMethodName, 148 MethodType samMethodType, 149 MethodHandle implMethod, 150 MethodType instantiatedMethodType, 151 boolean isSerializable, 152 Class<?>[] markerInterfaces, 153 MethodType[] additionalBridges) 154 throws LambdaConversionException { 155 super(caller, invokedType, samMethodName, samMethodType, 156 implMethod, instantiatedMethodType, 157 isSerializable, markerInterfaces, additionalBridges); 158 implMethodClassName = implDefiningClass.getName().replace('.', '/'); 159 implMethodName = implInfo.getName(); 160 implMethodDesc = implMethodType.toMethodDescriptorString(); 161 implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial) 162 ? implDefiningClass 163 : implMethodType.returnType(); 164 constructorType = invokedType.changeReturnType(Void.TYPE); 165 lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); 166 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 167 int parameterCount = invokedType.parameterCount(); 168 if (parameterCount > 0) { 169 argNames = new String[parameterCount]; 170 argDescs = new String[parameterCount]; 171 for (int i = 0; i < parameterCount; i++) { 172 argNames[i] = "arg$" + (i + 1); 173 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i)); 174 } 175 } else { 176 argNames = argDescs = EMPTY_STRING_ARRAY; 177 } 178 } 179 180 /** 181 * Build the CallSite. Generate a class file which implements the functional 182 * interface, define the class, if there are no parameters create an instance 183 * of the class which the CallSite will return, otherwise, generate handles 184 * which will call the class' constructor. 185 * 186 * @return a CallSite, which, when invoked, will return an instance of the 187 * functional interface 188 * @throws ReflectiveOperationException 189 * @throws LambdaConversionException If properly formed functional interface 190 * is not found 191 */ 192 @Override 193 CallSite buildCallSite() throws LambdaConversionException { 194 final Class<?> innerClass = spinInnerClass(); 195 if (invokedType.parameterCount() == 0) { 196 final Constructor<?>[] ctrs = AccessController.doPrivileged( 197 new PrivilegedAction<>() { 198 @Override 199 public Constructor<?>[] run() { 200 Constructor<?>[] ctrs = innerClass.getDeclaredConstructors(); 201 if (ctrs.length == 1) { 202 // The lambda implementing inner class constructor is private, set 203 // it accessible (by us) before creating the constant sole instance 204 ctrs[0].setAccessible(true); 205 } 206 return ctrs; 207 } 208 }); 209 if (ctrs.length != 1) { 210 throw new LambdaConversionException("Expected one lambda constructor for " 211 + innerClass.getCanonicalName() + ", got " + ctrs.length); 212 } 213 214 try { 215 Object inst = ctrs[0].newInstance(); 216 return new ConstantCallSite(MethodHandles.constant(samBase, inst)); 217 } 218 catch (ReflectiveOperationException e) { 219 throw new LambdaConversionException("Exception instantiating lambda object", e); 220 } 221 } else { 222 try { 223 UNSAFE.ensureClassInitialized(innerClass); 224 return new ConstantCallSite( 225 MethodHandles.Lookup.IMPL_LOOKUP 226 .findStatic(innerClass, NAME_FACTORY, invokedType)); 227 } 228 catch (ReflectiveOperationException e) { 229 throw new LambdaConversionException("Exception finding constructor", e); 230 } 231 } 232 } 233 234 /** 235 * Generate a class file which implements the functional 236 * interface, define and return the class. 237 * 238 * @implNote The class that is generated does not include signature 239 * information for exceptions that may be present on the SAM method. 240 * This is to reduce classfile size, and is harmless as checked exceptions 241 * are erased anyway, no one will ever compile against this classfile, 242 * and we make no guarantees about the reflective properties of lambda 243 * objects. 244 * 245 * @return a Class which implements the functional interface 246 * @throws LambdaConversionException If properly formed functional interface 247 * is not found 248 */ 249 private Class<?> spinInnerClass() throws LambdaConversionException { 250 String[] interfaces; 251 String samIntf = samBase.getName().replace('.', '/'); 252 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase); 253 if (markerInterfaces.length == 0) { 254 interfaces = new String[]{samIntf}; 255 } else { 256 // Assure no duplicate interfaces (ClassFormatError) 257 Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1); 258 itfs.add(samIntf); 259 for (Class<?> markerInterface : markerInterfaces) { 260 itfs.add(markerInterface.getName().replace('.', '/')); 261 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface); 262 } 263 interfaces = itfs.toArray(new String[itfs.size()]); 264 } 265 266 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, 267 lambdaClassName, null, 268 JAVA_LANG_OBJECT, interfaces); 269 270 // Generate final fields to be filled in by constructor 271 for (int i = 0; i < argDescs.length; i++) { 272 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, 273 argNames[i], 274 argDescs[i], 275 null, null); 276 fv.visitEnd(); 277 } 278 279 generateConstructor(); 280 281 if (invokedType.parameterCount() != 0) { 282 generateFactory(); 283 } 284 285 // Forward the SAM method 286 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, 287 samMethodType.toMethodDescriptorString(), null, null); 288 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); 289 new ForwardingMethodGenerator(mv).generate(samMethodType); 290 291 // Forward the bridges 292 if (additionalBridges != null) { 293 for (MethodType mt : additionalBridges) { 294 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, 295 mt.toMethodDescriptorString(), null, null); 296 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); 297 new ForwardingMethodGenerator(mv).generate(mt); 298 } 299 } 300 301 if (isSerializable) 302 generateSerializationFriendlyMethods(); 303 else if (accidentallySerializable) 304 generateSerializationHostileMethods(); 305 306 cw.visitEnd(); 307 308 // Define the generated class in this VM. 309 310 final byte[] classBytes = cw.toByteArray(); 311 312 // If requested, dump out to a file for debugging purposes 313 if (dumper != null) { 314 AccessController.doPrivileged(new PrivilegedAction<>() { 315 @Override 316 public Void run() { 317 dumper.dumpClass(lambdaClassName, classBytes); 318 return null; 319 } 320 }, null, 321 new FilePermission("<<ALL FILES>>", "read, write"), 322 // createDirectories may need it 323 new PropertyPermission("user.dir", "read")); 324 } 325 326 return UNSAFE.defineAnonymousClass(targetClass, classBytes, null); 327 } 328 329 /** 330 * Generate the factory method for the class 331 */ 332 private void generateFactory() { 333 MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null); 334 m.visitCode(); 335 m.visitTypeInsn(NEW, lambdaClassName); 336 m.visitInsn(Opcodes.DUP); 337 int parameterCount = invokedType.parameterCount(); 338 for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) { 339 Class<?> argType = invokedType.parameterType(typeIndex); 340 m.visitVarInsn(getLoadOpcode(argType), varIndex); 341 varIndex += getParameterSize(argType); 342 } 343 m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false); 344 m.visitInsn(ARETURN); 345 m.visitMaxs(-1, -1); 346 m.visitEnd(); 347 } 348 349 /** 350 * Generate the constructor for the class 351 */ 352 private void generateConstructor() { 353 // Generate constructor 354 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, 355 constructorType.toMethodDescriptorString(), null, null); 356 ctor.visitCode(); 357 ctor.visitVarInsn(ALOAD, 0); 358 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR, 359 METHOD_DESCRIPTOR_VOID, false); 360 int parameterCount = invokedType.parameterCount(); 361 for (int i = 0, lvIndex = 0; i < parameterCount; i++) { 362 ctor.visitVarInsn(ALOAD, 0); 363 Class<?> argType = invokedType.parameterType(i); 364 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1); 365 lvIndex += getParameterSize(argType); 366 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]); 367 } 368 ctor.visitInsn(RETURN); 369 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 370 ctor.visitMaxs(-1, -1); 371 ctor.visitEnd(); 372 } 373 374 /** 375 * Generate a writeReplace method that supports serialization 376 */ 377 private void generateSerializationFriendlyMethods() { 378 TypeConvertingMethodAdapter mv 379 = new TypeConvertingMethodAdapter( 380 cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 381 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, 382 null, null)); 383 384 mv.visitCode(); 385 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); 386 mv.visitInsn(DUP); 387 mv.visitLdcInsn(Type.getType(targetClass)); 388 mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); 389 mv.visitLdcInsn(samMethodName); 390 mv.visitLdcInsn(samMethodType.toMethodDescriptorString()); 391 mv.visitLdcInsn(implInfo.getReferenceKind()); 392 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/')); 393 mv.visitLdcInsn(implInfo.getName()); 394 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString()); 395 mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString()); 396 mv.iconst(argDescs.length); 397 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT); 398 for (int i = 0; i < argDescs.length; i++) { 399 mv.visitInsn(DUP); 400 mv.iconst(i); 401 mv.visitVarInsn(ALOAD, 0); 402 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); 403 mv.boxIfTypePrimitive(Type.getType(argDescs[i])); 404 mv.visitInsn(AASTORE); 405 } 406 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR, 407 DESCR_CTOR_SERIALIZED_LAMBDA, false); 408 mv.visitInsn(ARETURN); 409 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 410 mv.visitMaxs(-1, -1); 411 mv.visitEnd(); 412 } 413 414 /** 415 * Generate a readObject/writeObject method that is hostile to serialization 416 */ 417 private void generateSerializationHostileMethods() { 418 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 419 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT, 420 null, SER_HOSTILE_EXCEPTIONS); 421 mv.visitCode(); 422 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); 423 mv.visitInsn(DUP); 424 mv.visitLdcInsn("Non-serializable lambda"); 425 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, 426 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false); 427 mv.visitInsn(ATHROW); 428 mv.visitMaxs(-1, -1); 429 mv.visitEnd(); 430 431 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 432 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT, 433 null, SER_HOSTILE_EXCEPTIONS); 434 mv.visitCode(); 435 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); 436 mv.visitInsn(DUP); 437 mv.visitLdcInsn("Non-serializable lambda"); 438 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, 439 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false); 440 mv.visitInsn(ATHROW); 441 mv.visitMaxs(-1, -1); 442 mv.visitEnd(); 443 } 444 445 /** 446 * This class generates a method body which calls the lambda implementation 447 * method, converting arguments, as needed. 448 */ 449 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { 450 451 ForwardingMethodGenerator(MethodVisitor mv) { 452 super(mv); 453 } 454 455 void generate(MethodType methodType) { 456 visitCode(); 457 458 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 459 visitTypeInsn(NEW, implMethodClassName); 460 visitInsn(DUP); 461 } 462 for (int i = 0; i < argNames.length; i++) { 463 visitVarInsn(ALOAD, 0); 464 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); 465 } 466 467 convertArgumentTypes(methodType); 468 469 // Invoke the method we want to forward to 470 visitMethodInsn(invocationOpcode(), implMethodClassName, 471 implMethodName, implMethodDesc, 472 implDefiningClass.isInterface()); 473 474 // Convert the return value (if any) and return it 475 // Note: if adapting from non-void to void, the 'return' 476 // instruction will pop the unneeded result 477 Class<?> samReturnClass = methodType.returnType(); 478 convertType(implMethodReturnClass, samReturnClass, samReturnClass); 479 visitInsn(getReturnOpcode(samReturnClass)); 480 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored 481 visitMaxs(-1, -1); 482 visitEnd(); 483 } 484 485 private void convertArgumentTypes(MethodType samType) { 486 int lvIndex = 0; 487 boolean samIncludesReceiver = implIsInstanceMethod && 488 invokedType.parameterCount() == 0; 489 int samReceiverLength = samIncludesReceiver ? 1 : 0; 490 if (samIncludesReceiver) { 491 // push receiver 492 Class<?> rcvrType = samType.parameterType(0); 493 visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1); 494 lvIndex += getParameterSize(rcvrType); 495 convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0)); 496 } 497 int samParametersLength = samType.parameterCount(); 498 int argOffset = implMethodType.parameterCount() - samParametersLength; 499 for (int i = samReceiverLength; i < samParametersLength; i++) { 500 Class<?> argType = samType.parameterType(i); 501 visitVarInsn(getLoadOpcode(argType), lvIndex + 1); 502 lvIndex += getParameterSize(argType); 503 convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i)); 504 } 505 } 506 507 private int invocationOpcode() throws InternalError { 508 switch (implKind) { 509 case MethodHandleInfo.REF_invokeStatic: 510 return INVOKESTATIC; 511 case MethodHandleInfo.REF_newInvokeSpecial: 512 return INVOKESPECIAL; 513 case MethodHandleInfo.REF_invokeVirtual: 514 return INVOKEVIRTUAL; 515 case MethodHandleInfo.REF_invokeInterface: 516 return INVOKEINTERFACE; 517 case MethodHandleInfo.REF_invokeSpecial: 518 return INVOKESPECIAL; 519 default: 520 throw new InternalError("Unexpected invocation kind: " + implKind); 521 } 522 } 523 } 524 525 static int getParameterSize(Class<?> c) { 526 if (c == Void.TYPE) { 527 return 0; 528 } else if (c == Long.TYPE || c == Double.TYPE) { 529 return 2; 530 } 531 return 1; 532 } 533 534 static int getLoadOpcode(Class<?> c) { 535 if(c == Void.TYPE) { 536 throw new InternalError("Unexpected void type of load opcode"); 537 } 538 return ILOAD + getOpcodeOffset(c); 539 } 540 541 static int getReturnOpcode(Class<?> c) { 542 if(c == Void.TYPE) { 543 return RETURN; 544 } 545 return IRETURN + getOpcodeOffset(c); 546 } 547 548 private static int getOpcodeOffset(Class<?> c) { 549 if (c.isPrimitive()) { 550 if (c == Long.TYPE) { 551 return 1; 552 } else if (c == Float.TYPE) { 553 return 2; 554 } else if (c == Double.TYPE) { 555 return 3; 556 } 557 return 0; 558 } else { 559 return 4; 560 } 561 } 562 563 }