1 /* 2 * Copyright (C) 2017 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.common.base.CaseFormat.LOWER_CAMEL; 20 import static com.google.common.base.CaseFormat.UPPER_CAMEL; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType; 23 import static dagger.internal.codegen.DaggerStreams.toImmutableList; 24 import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod; 25 import static dagger.internal.codegen.RequestKinds.requestTypeName; 26 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding; 27 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType; 28 import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock; 29 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName; 30 import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom; 31 import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible; 32 import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible; 33 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 34 import static java.util.stream.Collectors.toList; 35 import static javax.lang.model.element.Modifier.STATIC; 36 import static javax.lang.model.type.TypeKind.VOID; 37 38 import com.google.auto.common.MoreElements; 39 import com.google.common.collect.ImmutableList; 40 import com.google.common.collect.ImmutableSet; 41 import com.squareup.javapoet.ClassName; 42 import com.squareup.javapoet.CodeBlock; 43 import com.squareup.javapoet.ParameterSpec; 44 import com.squareup.javapoet.TypeName; 45 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite; 46 import dagger.internal.codegen.javapoet.Expression; 47 import dagger.internal.codegen.langmodel.DaggerElements; 48 import dagger.internal.codegen.langmodel.DaggerTypes; 49 import dagger.model.DependencyRequest; 50 import dagger.model.RequestKind; 51 import java.util.ArrayList; 52 import java.util.List; 53 import java.util.Optional; 54 import java.util.function.Function; 55 import javax.lang.model.element.ExecutableElement; 56 import javax.lang.model.element.TypeElement; 57 import javax.lang.model.element.VariableElement; 58 import javax.lang.model.type.TypeKind; 59 import javax.lang.model.type.TypeMirror; 60 61 /** Convenience methods for creating and invoking {@link InjectionMethod}s. */ 62 final class InjectionMethods { 63 64 /** 65 * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed 66 * constructor. Its parameters match the dependency requests for constructor and members 67 * injection. 68 * 69 * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the 70 * {@code @Provides} method and its raw parameter types are publicly accessible, no method is 71 * necessary and this method returns {@link Optional#empty()}. 72 * 73 * <p>Example: 74 * 75 * <pre><code> 76 * abstract class FooModule { 77 * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … } 78 * } 79 * 80 * public static proxyProvideFoo(Bar bar, Baz baz) { … } 81 * </code></pre> 82 * 83 * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its 84 * raw parameter types are publicly accessible, no method is necessary and this method returns 85 * {@code Optional#empty()}. 86 * 87 * <p>Example: 88 * 89 * <pre><code> 90 * class Foo { 91 * {@literal @Inject} Foo(Bar bar) {} 92 * } 93 * 94 * public static Foo newFoo(Bar bar) { … } 95 * </code></pre> 96 */ 97 static final class ProvisionMethod { 98 /** 99 * Names of methods that are already defined in factories and shouldn't be used for the proxy 100 * method name. 101 */ 102 private static final ImmutableSet<String> BANNED_PROXY_NAMES = ImmutableSet.of("get", "create"); 103 104 /** 105 * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement() 106 * constructor} and injects the instance's members. 107 */ create( ProvisionBinding binding, CompilerOptions compilerOptions, DaggerElements elements)108 static InjectionMethod create( 109 ProvisionBinding binding, CompilerOptions compilerOptions, DaggerElements elements) { 110 ClassName proxyEnclosingClass = generatedClassNameForBinding(binding); 111 ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get()); 112 switch (element.getKind()) { 113 case CONSTRUCTOR: 114 return constructorProxy(proxyEnclosingClass, element, elements); 115 case METHOD: 116 return methodProxy( 117 proxyEnclosingClass, 118 element, 119 methodName(element), 120 ReceiverAccessibility.IGNORE, 121 CheckNotNullPolicy.get(binding, compilerOptions), 122 elements); 123 default: 124 throw new AssertionError(element); 125 } 126 } 127 128 /** 129 * Invokes the injection method for {@code binding}, with the dependencies transformed with the 130 * {@code dependencyUsage} function. 131 */ 132 // TODO(ronshapiro): Further extract a ProvisionMethod type that composes an InjectionMethod, so 133 // users can write ProvisionMethod.create().invoke() invoke( ProvisionBinding binding, Function<DependencyRequest, CodeBlock> dependencyUsage, ClassName requestingClass, Optional<CodeBlock> moduleReference, CompilerOptions compilerOptions, DaggerElements elements)134 static CodeBlock invoke( 135 ProvisionBinding binding, 136 Function<DependencyRequest, CodeBlock> dependencyUsage, 137 ClassName requestingClass, 138 Optional<CodeBlock> moduleReference, 139 CompilerOptions compilerOptions, 140 DaggerElements elements) { 141 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); 142 moduleReference.ifPresent(arguments::add); 143 arguments.addAll( 144 injectionMethodArguments( 145 binding.provisionDependencies(), dependencyUsage, requestingClass)); 146 // TODO(ronshapiro): make InjectionMethods @Injectable 147 return create(binding, compilerOptions, elements).invoke(arguments.build(), requestingClass); 148 } 149 constructorProxy( ClassName proxyEnclosingClass, ExecutableElement constructor, DaggerElements elements)150 private static InjectionMethod constructorProxy( 151 ClassName proxyEnclosingClass, ExecutableElement constructor, DaggerElements elements) { 152 TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement()); 153 InjectionMethod.Builder injectionMethod = 154 InjectionMethod.builder(elements) 155 .name(methodName(constructor)) 156 .returnType(enclosingType.asType()) 157 .enclosingClass(proxyEnclosingClass); 158 159 injectionMethod 160 .copyTypeParameters(enclosingType) 161 .copyThrows(constructor); 162 163 CodeBlock arguments = injectionMethod.copyParameters(constructor); 164 injectionMethod 165 .methodBodyBuilder() 166 .addStatement("return new $T($L)", enclosingType, arguments); 167 return injectionMethod.build(); 168 } 169 170 /** 171 * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage} 172 * requires the use of an injection method. 173 */ requiresInjectionMethod( ProvisionBinding binding, ImmutableList<Expression> arguments, CompilerOptions compilerOptions, String callingPackage, DaggerTypes types)174 static boolean requiresInjectionMethod( 175 ProvisionBinding binding, 176 ImmutableList<Expression> arguments, 177 CompilerOptions compilerOptions, 178 String callingPackage, 179 DaggerTypes types) { 180 ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get()); 181 return !binding.injectionSites().isEmpty() 182 || binding.shouldCheckForNull(compilerOptions) 183 || !isElementAccessibleFrom(method, callingPackage) 184 || !areParametersAssignable(method, arguments, types) 185 // This check should be removable once we drop support for -source 7 186 || method.getParameters().stream() 187 .map(VariableElement::asType) 188 .anyMatch(type -> !isRawTypeAccessible(type, callingPackage)); 189 } 190 areParametersAssignable( ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types)191 private static boolean areParametersAssignable( 192 ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types) { 193 List<? extends VariableElement> parameters = element.getParameters(); 194 checkArgument(parameters.size() == arguments.size()); 195 for (int i = 0; i < parameters.size(); i++) { 196 if (!types.isAssignable(arguments.get(i).type(), parameters.get(i).asType())) { 197 return false; 198 } 199 } 200 return true; 201 } 202 203 /** 204 * Returns the name of the {@code static} method that wraps {@code method}. For methods that are 205 * associated with {@code @Inject} constructors, the method will also inject all {@link 206 * InjectionSite}s. 207 */ methodName(ExecutableElement method)208 private static String methodName(ExecutableElement method) { 209 switch (method.getKind()) { 210 case CONSTRUCTOR: 211 return "newInstance"; 212 case METHOD: 213 String methodName = method.getSimpleName().toString(); 214 return BANNED_PROXY_NAMES.contains(methodName) 215 ? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, methodName) 216 : methodName; 217 default: 218 throw new AssertionError(method); 219 } 220 } 221 } 222 223 /** 224 * A static method that injects one member of an instance of a type. Its first parameter is an 225 * instance of the type to be injected. The remaining parameters match the dependency requests for 226 * the injection site. 227 * 228 * <p>Example: 229 * 230 * <pre><code> 231 * class Foo { 232 * {@literal @Inject} Bar bar; 233 * {@literal @Inject} void setThings(Baz baz, Qux qux) {} 234 * } 235 * 236 * public static injectBar(Foo instance, Bar bar) { … } 237 * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … } 238 * </code></pre> 239 */ 240 static final class InjectionSiteMethod { 241 /** 242 * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent 243 * that's in a different package), a method in the supertype's package must be generated to give 244 * the subclass's members injector a way to inject it. Each potentially inaccessible member 245 * receives its own method, as the subclass may need to inject them in a different order from 246 * the parent class. 247 */ create(InjectionSite injectionSite, DaggerElements elements)248 static InjectionMethod create(InjectionSite injectionSite, DaggerElements elements) { 249 String methodName = methodName(injectionSite); 250 ClassName proxyEnclosingClass = membersInjectorNameForType( 251 MoreElements.asType(injectionSite.element().getEnclosingElement())); 252 switch (injectionSite.kind()) { 253 case METHOD: 254 return methodProxy( 255 proxyEnclosingClass, 256 MoreElements.asExecutable(injectionSite.element()), 257 methodName, 258 ReceiverAccessibility.CAST_IF_NOT_PUBLIC, 259 CheckNotNullPolicy.IGNORE, 260 elements); 261 case FIELD: 262 return fieldProxy( 263 proxyEnclosingClass, 264 MoreElements.asVariable(injectionSite.element()), 265 methodName, 266 elements); 267 } 268 throw new AssertionError(injectionSite); 269 } 270 271 /** 272 * Invokes each of the injection methods for {@code injectionSites}, with the dependencies 273 * transformed using the {@code dependencyUsage} function. 274 * 275 * @param instanceType the type of the {@code instance} parameter 276 */ invokeAll( ImmutableSet<InjectionSite> injectionSites, ClassName generatedTypeName, CodeBlock instanceCodeBlock, TypeMirror instanceType, DaggerTypes types, Function<DependencyRequest, CodeBlock> dependencyUsage, DaggerElements elements)277 static CodeBlock invokeAll( 278 ImmutableSet<InjectionSite> injectionSites, 279 ClassName generatedTypeName, 280 CodeBlock instanceCodeBlock, 281 TypeMirror instanceType, 282 DaggerTypes types, 283 Function<DependencyRequest, CodeBlock> dependencyUsage, 284 DaggerElements elements) { 285 return injectionSites 286 .stream() 287 .map( 288 injectionSite -> { 289 TypeMirror injectSiteType = 290 types.erasure(injectionSite.element().getEnclosingElement().asType()); 291 292 // If instance has been declared as Object because it is not accessible from the 293 // component, but the injectionSite is in a supertype of instanceType that is 294 // publicly accessible, the InjectionSiteMethod will request the actual type and not 295 // Object as the first parameter. If so, cast to the supertype which is accessible 296 // from within generatedTypeName 297 CodeBlock maybeCastedInstance = 298 !types.isSubtype(instanceType, injectSiteType) 299 && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName()) 300 ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock) 301 : instanceCodeBlock; 302 return CodeBlock.of( 303 "$L;", 304 invoke( 305 injectionSite, 306 generatedTypeName, 307 maybeCastedInstance, 308 dependencyUsage, 309 elements)); 310 }) 311 .collect(toConcatenatedCodeBlock()); 312 } 313 314 /** 315 * Invokes the injection method for {@code injectionSite}, with the dependencies transformed 316 * using the {@code dependencyUsage} function. 317 */ invoke( InjectionSite injectionSite, ClassName generatedTypeName, CodeBlock instanceCodeBlock, Function<DependencyRequest, CodeBlock> dependencyUsage, DaggerElements elements)318 private static CodeBlock invoke( 319 InjectionSite injectionSite, 320 ClassName generatedTypeName, 321 CodeBlock instanceCodeBlock, 322 Function<DependencyRequest, CodeBlock> dependencyUsage, 323 DaggerElements elements) { 324 List<CodeBlock> arguments = new ArrayList<>(); 325 arguments.add(instanceCodeBlock); 326 if (!injectionSite.dependencies().isEmpty()) { 327 arguments.addAll( 328 injectionSite 329 .dependencies() 330 .stream() 331 .map(dependencyUsage) 332 .collect(toList())); 333 } 334 return create(injectionSite, elements).invoke(arguments, generatedTypeName); 335 } 336 337 /* 338 * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples: 339 * 340 * - @Inject void members() {} will generate a method that conflicts with the instance 341 * method `injectMembers(T)` 342 * - Adding the index could conflict with another member: 343 * @Inject void a(Object o) {} 344 * @Inject void a(String s) {} 345 * @Inject void a1(String s) {} 346 * 347 * Here, Method a(String) will add the suffix "1", which will conflict with the method 348 * generated for a1(String) 349 * - Members named "members" or "methods" could also conflict with the {@code static} injection 350 * method. 351 */ methodName(InjectionSite injectionSite)352 private static String methodName(InjectionSite injectionSite) { 353 int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName(); 354 String indexString = index == 0 ? "" : String.valueOf(index + 1); 355 return "inject" 356 + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString()) 357 + indexString; 358 } 359 } 360 361 /** 362 * Returns an argument list suitable for calling an injection method. Down-casts any arguments 363 * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method. 364 * 365 * @param dependencies the dependencies used by the method 366 * @param dependencyUsage function to apply on each of {@code dependencies} before casting 367 * @param requestingClass the class calling the injection method 368 */ injectionMethodArguments( ImmutableSet<DependencyRequest> dependencies, Function<DependencyRequest, CodeBlock> dependencyUsage, ClassName requestingClass)369 private static ImmutableList<CodeBlock> injectionMethodArguments( 370 ImmutableSet<DependencyRequest> dependencies, 371 Function<DependencyRequest, CodeBlock> dependencyUsage, 372 ClassName requestingClass) { 373 return dependencies.stream() 374 .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass)) 375 .collect(toImmutableList()); 376 } 377 injectionMethodArgument( DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName)378 private static CodeBlock injectionMethodArgument( 379 DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) { 380 TypeMirror keyType = dependency.key().type(); 381 CodeBlock.Builder codeBlock = CodeBlock.builder(); 382 if (!isRawTypeAccessible(keyType, generatedTypeName.packageName()) 383 && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) { 384 if (!dependency.kind().equals(RequestKind.INSTANCE)) { 385 TypeName usageTypeName = accessibleType(dependency); 386 codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName)); 387 } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) { 388 codeBlock.add("($T)", keyType); 389 } 390 } 391 return codeBlock.add(argument).build(); 392 } 393 394 /** 395 * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns 396 * {@link Object}. 397 */ accessibleType(DependencyRequest dependency)398 private static TypeName accessibleType(DependencyRequest dependency) { 399 TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type())); 400 return dependency 401 .requestElement() 402 .map(element -> element.asType().getKind().isPrimitive()) 403 .orElse(false) 404 ? typeName.unbox() 405 : typeName; 406 } 407 408 /** 409 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link 410 * Object}. 411 */ accessibleType(TypeMirror type)412 private static TypeName accessibleType(TypeMirror type) { 413 return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT; 414 } 415 416 /** 417 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link 418 * Object}. 419 */ 420 // TODO(ronshapiro): Can we use DaggerTypes.publiclyAccessibleType in place of this method? accessibleType(TypeMirror type, DaggerElements elements)421 private static TypeMirror accessibleType(TypeMirror type, DaggerElements elements) { 422 return isRawTypePubliclyAccessible(type) 423 ? type 424 : elements.getTypeElement(Object.class).asType(); 425 } 426 427 private enum ReceiverAccessibility { 428 CAST_IF_NOT_PUBLIC { 429 @Override parameterType(TypeMirror type, DaggerElements elements)430 TypeMirror parameterType(TypeMirror type, DaggerElements elements) { 431 return accessibleType(type, elements); 432 } 433 434 @Override potentiallyCast(CodeBlock instance, TypeMirror instanceType)435 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) { 436 return instanceWithPotentialCast(instance, instanceType); 437 } 438 }, 439 IGNORE { 440 @Override parameterType(TypeMirror type, DaggerElements elements)441 TypeMirror parameterType(TypeMirror type, DaggerElements elements) { 442 return type; 443 } 444 445 @Override potentiallyCast(CodeBlock instance, TypeMirror instanceType)446 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) { 447 return instance; 448 } 449 }, 450 ; 451 parameterType(TypeMirror type, DaggerElements elements)452 abstract TypeMirror parameterType(TypeMirror type, DaggerElements elements); potentiallyCast(CodeBlock instance, TypeMirror instanceType)453 abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType); 454 } 455 instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType)456 private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) { 457 return isRawTypePubliclyAccessible(instanceType) 458 ? instance 459 : CodeBlock.of("(($T) $L)", instanceType, instance); 460 } 461 462 private enum CheckNotNullPolicy { 463 IGNORE, CHECK_FOR_NULL; checkForNull(CodeBlock maybeNull)464 CodeBlock checkForNull(CodeBlock maybeNull) { 465 if (this.equals(IGNORE)) { 466 return maybeNull; 467 } 468 return checkNotNullProvidesMethod(maybeNull); 469 } 470 get(ProvisionBinding binding, CompilerOptions compilerOptions)471 static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) { 472 return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE; 473 } 474 } 475 methodProxy( ClassName proxyEnclosingClass, ExecutableElement method, String methodName, ReceiverAccessibility receiverAccessibility, CheckNotNullPolicy checkNotNullPolicy, DaggerElements elements)476 private static InjectionMethod methodProxy( 477 ClassName proxyEnclosingClass, 478 ExecutableElement method, 479 String methodName, 480 ReceiverAccessibility receiverAccessibility, 481 CheckNotNullPolicy checkNotNullPolicy, 482 DaggerElements elements) { 483 TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement()); 484 InjectionMethod.Builder injectionMethod = 485 InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass); 486 ParameterSpec instance = null; 487 if (!method.getModifiers().contains(STATIC)) { 488 instance = 489 injectionMethod.addParameter( 490 "instance", receiverAccessibility.parameterType(enclosingType.asType(), elements)); 491 } 492 493 CodeBlock arguments = injectionMethod.copyParameters(method); 494 if (!method.getReturnType().getKind().equals(VOID)) { 495 injectionMethod 496 .returnType(method.getReturnType()) 497 .nullableAnnotation(getNullableType(method)); 498 injectionMethod.methodBodyBuilder().add("return "); 499 } 500 CodeBlock.Builder proxyInvocation = CodeBlock.builder(); 501 if (method.getModifiers().contains(STATIC)) { 502 proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType()))); 503 } else { 504 injectionMethod.copyTypeParameters(enclosingType); 505 proxyInvocation.add( 506 receiverAccessibility.potentiallyCast( 507 CodeBlock.of("$N", instance), enclosingType.asType())); 508 } 509 510 injectionMethod 511 .copyTypeParameters(method) 512 .copyThrows(method); 513 514 proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments); 515 injectionMethod 516 .methodBodyBuilder() 517 .add(checkNotNullPolicy.checkForNull(proxyInvocation.build())) 518 .add(";\n"); 519 return injectionMethod.build(); 520 } 521 fieldProxy( ClassName proxyEnclosingClass, VariableElement field, String methodName, DaggerElements elements)522 private static InjectionMethod fieldProxy( 523 ClassName proxyEnclosingClass, 524 VariableElement field, 525 String methodName, 526 DaggerElements elements) { 527 TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement()); 528 InjectionMethod.Builder injectionMethod = 529 InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass); 530 injectionMethod.copyTypeParameters(enclosingType); 531 532 ParameterSpec instance = 533 injectionMethod.addParameter("instance", accessibleType(enclosingType.asType(), elements)); 534 CodeBlock parameter = injectionMethod.copyParameter(field); 535 injectionMethod 536 .methodBodyBuilder() 537 .addStatement( 538 "$L.$L = $L", 539 instanceWithPotentialCast(CodeBlock.of("$N", instance), enclosingType.asType()), 540 field.getSimpleName(), 541 parameter); 542 return injectionMethod.build(); 543 } 544 } 545