You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CtBehavior.java 45KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist;
  17. import javassist.bytecode.AccessFlag;
  18. import javassist.bytecode.AnnotationsAttribute;
  19. import javassist.bytecode.AttributeInfo;
  20. import javassist.bytecode.BadBytecode;
  21. import javassist.bytecode.Bytecode;
  22. import javassist.bytecode.CodeAttribute;
  23. import javassist.bytecode.CodeIterator;
  24. import javassist.bytecode.ConstPool;
  25. import javassist.bytecode.Descriptor;
  26. import javassist.bytecode.ExceptionsAttribute;
  27. import javassist.bytecode.LineNumberAttribute;
  28. import javassist.bytecode.LocalVariableAttribute;
  29. import javassist.bytecode.LocalVariableTypeAttribute;
  30. import javassist.bytecode.MethodInfo;
  31. import javassist.bytecode.Opcode;
  32. import javassist.bytecode.ParameterAnnotationsAttribute;
  33. import javassist.bytecode.SignatureAttribute;
  34. import javassist.bytecode.StackMap;
  35. import javassist.bytecode.StackMapTable;
  36. import javassist.compiler.CompileError;
  37. import javassist.compiler.Javac;
  38. import javassist.expr.ExprEditor;
  39. /**
  40. * <code>CtBehavior</code> represents a method, a constructor,
  41. * or a static constructor (class initializer).
  42. * It is the abstract super class of
  43. * <code>CtMethod</code> and <code>CtConstructor</code>.
  44. *
  45. * <p>To directly read or modify bytecode, obtain <code>MethodInfo</code>
  46. * objects.
  47. *
  48. * @see #getMethodInfo()
  49. */
  50. public abstract class CtBehavior extends CtMember {
  51. protected MethodInfo methodInfo;
  52. protected CtBehavior(CtClass clazz, MethodInfo minfo) {
  53. super(clazz);
  54. methodInfo = minfo;
  55. }
  56. /**
  57. * @param isCons true if this is a constructor.
  58. */
  59. void copy(CtBehavior src, boolean isCons, ClassMap map)
  60. throws CannotCompileException
  61. {
  62. CtClass declaring = declaringClass;
  63. MethodInfo srcInfo = src.methodInfo;
  64. CtClass srcClass = src.getDeclaringClass();
  65. ConstPool cp = declaring.getClassFile2().getConstPool();
  66. map = new ClassMap(map);
  67. map.put(srcClass.getName(), declaring.getName());
  68. try {
  69. boolean patch = false;
  70. CtClass srcSuper = srcClass.getSuperclass();
  71. CtClass destSuper = declaring.getSuperclass();
  72. String destSuperName = null;
  73. if (srcSuper != null && destSuper != null) {
  74. String srcSuperName = srcSuper.getName();
  75. destSuperName = destSuper.getName();
  76. if (!srcSuperName.equals(destSuperName))
  77. if (srcSuperName.equals(CtClass.javaLangObject))
  78. patch = true;
  79. else
  80. map.putIfNone(srcSuperName, destSuperName);
  81. }
  82. // a stack map table is copied from srcInfo.
  83. methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
  84. if (isCons && patch)
  85. methodInfo.setSuperclass(destSuperName);
  86. }
  87. catch (NotFoundException e) {
  88. throw new CannotCompileException(e);
  89. }
  90. catch (BadBytecode e) {
  91. throw new CannotCompileException(e);
  92. }
  93. }
  94. @Override
  95. protected void extendToString(StringBuffer buffer) {
  96. buffer.append(' ');
  97. buffer.append(getName());
  98. buffer.append(' ');
  99. buffer.append(methodInfo.getDescriptor());
  100. }
  101. /**
  102. * Returns the method or constructor name followed by parameter types
  103. * such as <code>javassist.CtBehavior.stBody(String)</code>.
  104. *
  105. * @since 3.5
  106. */
  107. public abstract String getLongName();
  108. /**
  109. * Returns the <code>MethodInfo</code> representing this method/constructor in the
  110. * class file.
  111. *
  112. * <p>If you modify the bytecode through the returned
  113. * <code>MethodInfo</code> object, you might have to explicitly
  114. * rebuild a stack map table. Javassist does not automatically
  115. * rebuild it for avoiding unnecessary rebuilding.
  116. *
  117. * @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool)
  118. */
  119. public MethodInfo getMethodInfo() {
  120. declaringClass.checkModify();
  121. return methodInfo;
  122. }
  123. /**
  124. * Returns the <code>MethodInfo</code> representing the method/constructor in the
  125. * class file (read only).
  126. * Normal applications do not need calling this method. Use
  127. * <code>getMethodInfo()</code>.
  128. *
  129. * <p>The <code>MethodInfo</code> object obtained by this method
  130. * is read only. Changes to this object might not be reflected
  131. * on a class file generated by <code>toBytecode()</code>,
  132. * <code>toClass()</code>, etc in <code>CtClass</code>.
  133. *
  134. * <p>This method is available even if the <code>CtClass</code>
  135. * containing this method is frozen. However, if the class is
  136. * frozen, the <code>MethodInfo</code> might be also pruned.
  137. *
  138. * @see #getMethodInfo()
  139. * @see CtClass#isFrozen()
  140. * @see CtClass#prune()
  141. */
  142. public MethodInfo getMethodInfo2() { return methodInfo; }
  143. /**
  144. * Obtains the modifiers of the method/constructor.
  145. *
  146. * @return modifiers encoded with
  147. * <code>javassist.Modifier</code>.
  148. * @see Modifier
  149. */
  150. @Override
  151. public int getModifiers() {
  152. return AccessFlag.toModifier(methodInfo.getAccessFlags());
  153. }
  154. /**
  155. * Sets the encoded modifiers of the method/constructor.
  156. *
  157. * <p>Changing the modifiers may cause a problem.
  158. * For example, if a non-static method is changed to static,
  159. * the method will be rejected by the bytecode verifier.
  160. *
  161. * @see Modifier
  162. */
  163. @Override
  164. public void setModifiers(int mod) {
  165. declaringClass.checkModify();
  166. methodInfo.setAccessFlags(AccessFlag.of(mod));
  167. }
  168. /**
  169. * Returns true if the class has the specified annotation type.
  170. *
  171. * @param typeName the name of annotation type.
  172. * @return <code>true</code> if the annotation is found,
  173. * otherwise <code>false</code>.
  174. * @since 3.21
  175. */
  176. @Override
  177. public boolean hasAnnotation(String typeName) {
  178. MethodInfo mi = getMethodInfo2();
  179. AnnotationsAttribute ainfo = (AnnotationsAttribute)
  180. mi.getAttribute(AnnotationsAttribute.invisibleTag);
  181. AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
  182. mi.getAttribute(AnnotationsAttribute.visibleTag);
  183. return CtClassType.hasAnnotationType(typeName,
  184. getDeclaringClass().getClassPool(),
  185. ainfo, ainfo2);
  186. }
  187. /**
  188. * Returns the annotation if the class has the specified annotation class.
  189. * For example, if an annotation <code>@Author</code> is associated
  190. * with this method/constructor, an <code>Author</code> object is returned.
  191. * The member values can be obtained by calling methods on
  192. * the <code>Author</code> object.
  193. *
  194. * @param clz the annotation class.
  195. * @return the annotation if found, otherwise <code>null</code>.
  196. * @since 3.11
  197. */
  198. @Override
  199. public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
  200. MethodInfo mi = getMethodInfo2();
  201. AnnotationsAttribute ainfo = (AnnotationsAttribute)
  202. mi.getAttribute(AnnotationsAttribute.invisibleTag);
  203. AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
  204. mi.getAttribute(AnnotationsAttribute.visibleTag);
  205. return CtClassType.getAnnotationType(clz,
  206. getDeclaringClass().getClassPool(),
  207. ainfo, ainfo2);
  208. }
  209. /**
  210. * Returns the annotations associated with this method or constructor.
  211. *
  212. * @return an array of annotation-type objects.
  213. * @see #getAvailableAnnotations()
  214. * @since 3.1
  215. */
  216. @Override
  217. public Object[] getAnnotations() throws ClassNotFoundException {
  218. return getAnnotations(false);
  219. }
  220. /**
  221. * Returns the annotations associated with this method or constructor.
  222. * If any annotations are not on the classpath, they are not included
  223. * in the returned array.
  224. *
  225. * @return an array of annotation-type objects.
  226. * @see #getAnnotations()
  227. * @since 3.3
  228. */
  229. @Override
  230. public Object[] getAvailableAnnotations(){
  231. try{
  232. return getAnnotations(true);
  233. }
  234. catch (ClassNotFoundException e){
  235. throw new RuntimeException("Unexpected exception", e);
  236. }
  237. }
  238. private Object[] getAnnotations(boolean ignoreNotFound)
  239. throws ClassNotFoundException
  240. {
  241. MethodInfo mi = getMethodInfo2();
  242. AnnotationsAttribute ainfo = (AnnotationsAttribute)
  243. mi.getAttribute(AnnotationsAttribute.invisibleTag);
  244. AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
  245. mi.getAttribute(AnnotationsAttribute.visibleTag);
  246. return CtClassType.toAnnotationType(ignoreNotFound,
  247. getDeclaringClass().getClassPool(),
  248. ainfo, ainfo2);
  249. }
  250. /**
  251. * Returns the parameter annotations associated with this method or constructor.
  252. *
  253. * @return an array of annotation-type objects. The length of the returned array is
  254. * equal to the number of the formal parameters. If each parameter has no
  255. * annotation, the elements of the returned array are empty arrays.
  256. *
  257. * @see #getAvailableParameterAnnotations()
  258. * @see #getAnnotations()
  259. * @since 3.1
  260. */
  261. public Object[][] getParameterAnnotations() throws ClassNotFoundException {
  262. return getParameterAnnotations(false);
  263. }
  264. /**
  265. * Returns the parameter annotations associated with this method or constructor.
  266. * If any annotations are not on the classpath, they are not included in the
  267. * returned array.
  268. *
  269. * @return an array of annotation-type objects. The length of the returned array is
  270. * equal to the number of the formal parameters. If each parameter has no
  271. * annotation, the elements of the returned array are empty arrays.
  272. *
  273. * @see #getParameterAnnotations()
  274. * @see #getAvailableAnnotations()
  275. * @since 3.3
  276. */
  277. public Object[][] getAvailableParameterAnnotations(){
  278. try {
  279. return getParameterAnnotations(true);
  280. }
  281. catch(ClassNotFoundException e) {
  282. throw new RuntimeException("Unexpected exception", e);
  283. }
  284. }
  285. Object[][] getParameterAnnotations(boolean ignoreNotFound)
  286. throws ClassNotFoundException
  287. {
  288. MethodInfo mi = getMethodInfo2();
  289. ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute)
  290. mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
  291. ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute)
  292. mi.getAttribute(ParameterAnnotationsAttribute.visibleTag);
  293. return CtClassType.toAnnotationType(ignoreNotFound,
  294. getDeclaringClass().getClassPool(),
  295. ainfo, ainfo2, mi);
  296. }
  297. /**
  298. * Obtains parameter types of this method/constructor.
  299. */
  300. public CtClass[] getParameterTypes() throws NotFoundException {
  301. return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
  302. declaringClass.getClassPool());
  303. }
  304. /**
  305. * Obtains the type of the returned value.
  306. */
  307. CtClass getReturnType0() throws NotFoundException {
  308. return Descriptor.getReturnType(methodInfo.getDescriptor(),
  309. declaringClass.getClassPool());
  310. }
  311. /**
  312. * Returns the method signature (the parameter types
  313. * and the return type).
  314. * The method signature is represented by a character string
  315. * called method descriptor, which is defined in the JVM specification.
  316. * If two methods/constructors have
  317. * the same parameter types
  318. * and the return type, <code>getSignature()</code> returns the
  319. * same string (the return type of constructors is <code>void</code>).
  320. *
  321. * <p>Note that the returned string is not the type signature
  322. * contained in the <code>SignatureAttirbute</code>. It is
  323. * a descriptor.
  324. *
  325. * @see javassist.bytecode.Descriptor
  326. * @see #getGenericSignature()
  327. */
  328. @Override
  329. public String getSignature() {
  330. return methodInfo.getDescriptor();
  331. }
  332. /**
  333. * Returns the generic signature of the method.
  334. * It represents parameter types including type variables.
  335. *
  336. * @see SignatureAttribute#toMethodSignature(String)
  337. * @since 3.17
  338. */
  339. @Override
  340. public String getGenericSignature() {
  341. SignatureAttribute sa
  342. = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag);
  343. return sa == null ? null : sa.getSignature();
  344. }
  345. /**
  346. * Set the generic signature of the method.
  347. * It represents parameter types including type variables.
  348. * See {@link javassist.CtClass#setGenericSignature(String)}
  349. * for a code sample.
  350. *
  351. * @param sig a new generic signature.
  352. * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
  353. * @since 3.17
  354. */
  355. @Override
  356. public void setGenericSignature(String sig) {
  357. declaringClass.checkModify();
  358. methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig));
  359. }
  360. /**
  361. * Obtains exceptions that this method/constructor may throw.
  362. *
  363. * @return a zero-length array if there is no throws clause.
  364. */
  365. public CtClass[] getExceptionTypes() throws NotFoundException {
  366. String[] exceptions;
  367. ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
  368. if (ea == null)
  369. exceptions = null;
  370. else
  371. exceptions = ea.getExceptions();
  372. return declaringClass.getClassPool().get(exceptions);
  373. }
  374. /**
  375. * Sets exceptions that this method/constructor may throw.
  376. */
  377. public void setExceptionTypes(CtClass[] types) throws NotFoundException {
  378. declaringClass.checkModify();
  379. if (types == null || types.length == 0) {
  380. methodInfo.removeExceptionsAttribute();
  381. return;
  382. }
  383. String[] names = new String[types.length];
  384. for (int i = 0; i < types.length; ++i)
  385. names[i] = types[i].getName();
  386. ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
  387. if (ea == null) {
  388. ea = new ExceptionsAttribute(methodInfo.getConstPool());
  389. methodInfo.setExceptionsAttribute(ea);
  390. }
  391. ea.setExceptions(names);
  392. }
  393. /**
  394. * Returns true if the body is empty.
  395. */
  396. public abstract boolean isEmpty();
  397. /**
  398. * Sets a method/constructor body.
  399. *
  400. * @param src the source code representing the body.
  401. * It must be a single statement or block.
  402. * If it is <code>null</code>, the substituted
  403. * body does nothing except returning zero or null.
  404. */
  405. public void setBody(String src) throws CannotCompileException {
  406. setBody(src, null, null);
  407. }
  408. /**
  409. * Sets a method/constructor body.
  410. *
  411. * @param src the source code representing the body.
  412. * It must be a single statement or block.
  413. * If it is <code>null</code>, the substituted
  414. * body does nothing except returning zero or null.
  415. * @param delegateObj the source text specifying the object
  416. * that is called on by <code>$proceed()</code>.
  417. * @param delegateMethod the name of the method
  418. * that is called by <code>$proceed()</code>.
  419. */
  420. public void setBody(String src,
  421. String delegateObj, String delegateMethod)
  422. throws CannotCompileException
  423. {
  424. CtClass cc = declaringClass;
  425. cc.checkModify();
  426. try {
  427. Javac jv = new Javac(cc);
  428. if (delegateMethod != null)
  429. jv.recordProceed(delegateObj, delegateMethod);
  430. Bytecode b = jv.compileBody(this, src);
  431. methodInfo.setCodeAttribute(b.toCodeAttribute());
  432. methodInfo.setAccessFlags(methodInfo.getAccessFlags()
  433. & ~AccessFlag.ABSTRACT);
  434. methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
  435. declaringClass.rebuildClassFile();
  436. }
  437. catch (CompileError e) {
  438. throw new CannotCompileException(e);
  439. } catch (BadBytecode e) {
  440. throw new CannotCompileException(e);
  441. }
  442. }
  443. static void setBody0(CtClass srcClass, MethodInfo srcInfo,
  444. CtClass destClass, MethodInfo destInfo,
  445. ClassMap map)
  446. throws CannotCompileException
  447. {
  448. destClass.checkModify();
  449. map = new ClassMap(map);
  450. map.put(srcClass.getName(), destClass.getName());
  451. try {
  452. CodeAttribute cattr = srcInfo.getCodeAttribute();
  453. if (cattr != null) {
  454. ConstPool cp = destInfo.getConstPool();
  455. CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
  456. destInfo.setCodeAttribute(ca);
  457. // a stack map table is copied to destInfo.
  458. }
  459. }
  460. catch (CodeAttribute.RuntimeCopyException e) {
  461. /* the exception may be thrown by copy() in CodeAttribute.
  462. */
  463. throw new CannotCompileException(e);
  464. }
  465. destInfo.setAccessFlags(destInfo.getAccessFlags()
  466. & ~AccessFlag.ABSTRACT);
  467. destClass.rebuildClassFile();
  468. }
  469. /**
  470. * Obtains an attribute with the given name.
  471. * If that attribute is not found in the class file, this
  472. * method returns null.
  473. *
  474. * <p>Note that an attribute is a data block specified by
  475. * the class file format. It is not an annotation.
  476. * See {@link javassist.bytecode.AttributeInfo}.
  477. *
  478. * @param name attribute name
  479. */
  480. @Override
  481. public byte[] getAttribute(String name)
  482. {
  483. AttributeInfo ai = methodInfo.getAttribute(name);
  484. if (ai == null)
  485. return null;
  486. return ai.get();
  487. }
  488. /**
  489. * Adds an attribute. The attribute is saved in the class file.
  490. *
  491. * <p>Note that an attribute is a data block specified by
  492. * the class file format. It is not an annotation.
  493. * See {@link javassist.bytecode.AttributeInfo}.
  494. *
  495. * @param name attribute name
  496. * @param data attribute value
  497. */
  498. @Override
  499. public void setAttribute(String name, byte[] data)
  500. {
  501. declaringClass.checkModify();
  502. methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
  503. name, data));
  504. }
  505. /**
  506. * Declares to use <code>$cflow</code> for this method/constructor.
  507. * If <code>$cflow</code> is used, the class files modified
  508. * with Javassist requires a support class
  509. * <code>javassist.runtime.Cflow</code> at runtime
  510. * (other Javassist classes are not required at runtime).
  511. *
  512. * <p>Every <code>$cflow</code> variable is given a unique name.
  513. * For example, if the given name is <code>"Point.paint"</code>,
  514. * then the variable is indicated by <code>$cflow(Point.paint)</code>.
  515. *
  516. * @param name <code>$cflow</code> name. It can include
  517. * alphabets, numbers, <code>_</code>,
  518. * <code>$</code>, and <code>.</code> (dot).
  519. *
  520. * @see javassist.runtime.Cflow
  521. */
  522. public void useCflow(String name) throws CannotCompileException
  523. {
  524. CtClass cc = declaringClass;
  525. cc.checkModify();
  526. ClassPool pool = cc.getClassPool();
  527. String fname;
  528. int i = 0;
  529. while (true) {
  530. fname = "_cflow$" + i++;
  531. try {
  532. cc.getDeclaredField(fname);
  533. }
  534. catch(NotFoundException e) {
  535. break;
  536. }
  537. }
  538. pool.recordCflow(name, declaringClass.getName(), fname);
  539. try {
  540. CtClass type = pool.get("javassist.runtime.Cflow");
  541. CtField field = new CtField(type, fname, cc);
  542. field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
  543. cc.addField(field, CtField.Initializer.byNew(type));
  544. insertBefore(fname + ".enter();", false);
  545. String src = fname + ".exit();";
  546. insertAfter(src, true);
  547. }
  548. catch (NotFoundException e) {
  549. throw new CannotCompileException(e);
  550. }
  551. }
  552. /**
  553. * Declares a new local variable. The scope of this variable is the
  554. * whole method body. The initial value of that variable is not set.
  555. * The declared variable can be accessed in the code snippet inserted
  556. * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
  557. *
  558. * <p>If the second parameter <code>asFinally</code> to
  559. * <code>insertAfter()</code> is true, the declared local variable
  560. * is not visible from the code inserted by <code>insertAfter()</code>.
  561. *
  562. * @param name the name of the variable
  563. * @param type the type of the variable
  564. * @see #insertBefore(String)
  565. * @see #insertAfter(String)
  566. */
  567. public void addLocalVariable(String name, CtClass type)
  568. throws CannotCompileException
  569. {
  570. declaringClass.checkModify();
  571. ConstPool cp = methodInfo.getConstPool();
  572. CodeAttribute ca = methodInfo.getCodeAttribute();
  573. if (ca == null)
  574. throw new CannotCompileException("no method body");
  575. LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(
  576. LocalVariableAttribute.tag);
  577. if (va == null) {
  578. va = new LocalVariableAttribute(cp);
  579. ca.getAttributes().add(va);
  580. }
  581. int maxLocals = ca.getMaxLocals();
  582. String desc = Descriptor.of(type);
  583. va.addEntry(0, ca.getCodeLength(),
  584. cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals);
  585. ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
  586. }
  587. /**
  588. * Inserts a new parameter, which becomes the first parameter.
  589. */
  590. public void insertParameter(CtClass type)
  591. throws CannotCompileException
  592. {
  593. declaringClass.checkModify();
  594. String desc = methodInfo.getDescriptor();
  595. String desc2 = Descriptor.insertParameter(type, desc);
  596. try {
  597. addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc);
  598. }
  599. catch (BadBytecode e) {
  600. throw new CannotCompileException(e);
  601. }
  602. methodInfo.setDescriptor(desc2);
  603. }
  604. /**
  605. * Appends a new parameter, which becomes the last parameter.
  606. */
  607. public void addParameter(CtClass type)
  608. throws CannotCompileException
  609. {
  610. declaringClass.checkModify();
  611. String desc = methodInfo.getDescriptor();
  612. String desc2 = Descriptor.appendParameter(type, desc);
  613. int offset = Modifier.isStatic(getModifiers()) ? 0 : 1;
  614. try {
  615. addParameter2(offset + Descriptor.paramSize(desc), type, desc);
  616. }
  617. catch (BadBytecode e) {
  618. throw new CannotCompileException(e);
  619. }
  620. methodInfo.setDescriptor(desc2);
  621. }
  622. private void addParameter2(int where, CtClass type, String desc)
  623. throws BadBytecode
  624. {
  625. CodeAttribute ca = methodInfo.getCodeAttribute();
  626. if (ca != null) {
  627. int size = 1;
  628. char typeDesc = 'L';
  629. int classInfo = 0;
  630. if (type.isPrimitive()) {
  631. CtPrimitiveType cpt = (CtPrimitiveType)type;
  632. size = cpt.getDataSize();
  633. typeDesc = cpt.getDescriptor();
  634. }
  635. else
  636. classInfo = methodInfo.getConstPool().addClassInfo(type);
  637. ca.insertLocalVar(where, size);
  638. LocalVariableAttribute va
  639. = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag);
  640. if (va != null)
  641. va.shiftIndex(where, size);
  642. LocalVariableTypeAttribute lvta
  643. = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag);
  644. if (lvta != null)
  645. lvta.shiftIndex(where, size);
  646. StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
  647. if (smt != null)
  648. smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
  649. StackMap sm = (StackMap)ca.getAttribute(StackMap.tag);
  650. if (sm != null)
  651. sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
  652. }
  653. }
  654. /**
  655. * Modifies the method/constructor body.
  656. *
  657. * @param converter specifies how to modify.
  658. */
  659. public void instrument(CodeConverter converter)
  660. throws CannotCompileException
  661. {
  662. declaringClass.checkModify();
  663. ConstPool cp = methodInfo.getConstPool();
  664. converter.doit(getDeclaringClass(), methodInfo, cp);
  665. }
  666. /**
  667. * Modifies the method/constructor body.
  668. *
  669. * <p>While executing this method, only <code>replace()</code>
  670. * in <code>Expr</code> is available for bytecode modification.
  671. * Other methods such as <code>insertBefore()</code> may collapse
  672. * the bytecode because the <code>ExprEditor</code> loses
  673. * its current position.
  674. *
  675. * @param editor specifies how to modify.
  676. * @see javassist.expr.Expr#replace(String)
  677. * @see #insertBefore(String)
  678. */
  679. public void instrument(ExprEditor editor)
  680. throws CannotCompileException
  681. {
  682. // if the class is not frozen,
  683. // does not turn the modified flag on.
  684. if (declaringClass.isFrozen())
  685. declaringClass.checkModify();
  686. if (editor.doit(declaringClass, methodInfo))
  687. declaringClass.checkModify();
  688. }
  689. /**
  690. * Inserts bytecode at the beginning of the body.
  691. *
  692. * <p>If this object represents a constructor,
  693. * the bytecode is inserted before
  694. * a constructor in the super class or this class is called.
  695. * Therefore, the inserted bytecode is subject to constraints described
  696. * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
  697. * For example, it cannot access instance fields or methods although
  698. * it may assign a value to an instance field directly declared in this
  699. * class. Accessing static fields and methods is allowed.
  700. * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
  701. *
  702. * @param src the source code representing the inserted bytecode.
  703. * It must be a single statement or block.
  704. * @see CtConstructor#insertBeforeBody(String)
  705. */
  706. public void insertBefore(String src) throws CannotCompileException {
  707. insertBefore(src, true);
  708. }
  709. private void insertBefore(String src, boolean rebuild)
  710. throws CannotCompileException
  711. {
  712. CtClass cc = declaringClass;
  713. cc.checkModify();
  714. CodeAttribute ca = methodInfo.getCodeAttribute();
  715. if (ca == null)
  716. throw new CannotCompileException("no method body");
  717. CodeIterator iterator = ca.iterator();
  718. Javac jv = new Javac(cc);
  719. try {
  720. int nvars = jv.recordParams(getParameterTypes(),
  721. Modifier.isStatic(getModifiers()));
  722. jv.recordParamNames(ca, nvars);
  723. jv.recordLocalVariables(ca, 0);
  724. jv.recordReturnType(getReturnType0(), false);
  725. jv.compileStmnt(src);
  726. Bytecode b = jv.getBytecode();
  727. int stack = b.getMaxStack();
  728. int locals = b.getMaxLocals();
  729. if (stack > ca.getMaxStack())
  730. ca.setMaxStack(stack);
  731. if (locals > ca.getMaxLocals())
  732. ca.setMaxLocals(locals);
  733. int pos = iterator.insertEx(b.get());
  734. iterator.insert(b.getExceptionTable(), pos);
  735. if (rebuild)
  736. methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
  737. }
  738. catch (NotFoundException e) {
  739. throw new CannotCompileException(e);
  740. }
  741. catch (CompileError e) {
  742. throw new CannotCompileException(e);
  743. }
  744. catch (BadBytecode e) {
  745. throw new CannotCompileException(e);
  746. }
  747. }
  748. /**
  749. * Inserts bytecode at the end of the body.
  750. * The bytecode is inserted just before every return insturction.
  751. * It is not executed when an exception is thrown.
  752. *
  753. * @param src the source code representing the inserted bytecode.
  754. * It must be a single statement or block.
  755. */
  756. public void insertAfter(String src)
  757. throws CannotCompileException
  758. {
  759. insertAfter(src, false);
  760. }
  761. /**
  762. * Inserts bytecode at the end of the body.
  763. * The bytecode is inserted just before every return insturction.
  764. *
  765. * @param src the source code representing the inserted bytecode.
  766. * It must be a single statement or block.
  767. * @param asFinally true if the inserted bytecode is executed
  768. * not only when the control normally returns
  769. * but also when an exception is thrown.
  770. * If this parameter is true, the inserted code cannot
  771. * access local variables.
  772. */
  773. public void insertAfter(String src, boolean asFinally)
  774. throws CannotCompileException
  775. {
  776. CtClass cc = declaringClass;
  777. cc.checkModify();
  778. ConstPool pool = methodInfo.getConstPool();
  779. CodeAttribute ca = methodInfo.getCodeAttribute();
  780. if (ca == null)
  781. throw new CannotCompileException("no method body");
  782. CodeIterator iterator = ca.iterator();
  783. int retAddr = ca.getMaxLocals();
  784. Bytecode b = new Bytecode(pool, 0, retAddr + 1);
  785. b.setStackDepth(ca.getMaxStack() + 1);
  786. Javac jv = new Javac(b, cc);
  787. try {
  788. int nvars = jv.recordParams(getParameterTypes(),
  789. Modifier.isStatic(getModifiers()));
  790. jv.recordParamNames(ca, nvars);
  791. CtClass rtype = getReturnType0();
  792. int varNo = jv.recordReturnType(rtype, true);
  793. jv.recordLocalVariables(ca, 0);
  794. // finally clause for exceptions
  795. int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
  796. jv, src);
  797. int handlerPos = iterator.getCodeLength();
  798. if (asFinally)
  799. ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0);
  800. int adviceLen = 0;
  801. int advicePos = 0;
  802. boolean noReturn = true;
  803. while (iterator.hasNext()) {
  804. int pos = iterator.next();
  805. if (pos >= handlerPos)
  806. break;
  807. int c = iterator.byteAt(pos);
  808. if (c == Opcode.ARETURN || c == Opcode.IRETURN
  809. || c == Opcode.FRETURN || c == Opcode.LRETURN
  810. || c == Opcode.DRETURN || c == Opcode.RETURN) {
  811. if (noReturn) {
  812. // finally clause for normal termination
  813. adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo);
  814. handlerPos = iterator.append(b.get());
  815. iterator.append(b.getExceptionTable(), handlerPos);
  816. advicePos = iterator.getCodeLength() - adviceLen;
  817. handlerLen = advicePos - handlerPos;
  818. noReturn = false;
  819. }
  820. insertGoto(iterator, advicePos, pos);
  821. advicePos = iterator.getCodeLength() - adviceLen;
  822. handlerPos = advicePos - handlerLen;
  823. }
  824. }
  825. if (noReturn) {
  826. handlerPos = iterator.append(b.get());
  827. iterator.append(b.getExceptionTable(), handlerPos);
  828. }
  829. ca.setMaxStack(b.getMaxStack());
  830. ca.setMaxLocals(b.getMaxLocals());
  831. methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
  832. }
  833. catch (NotFoundException e) {
  834. throw new CannotCompileException(e);
  835. }
  836. catch (CompileError e) {
  837. throw new CannotCompileException(e);
  838. }
  839. catch (BadBytecode e) {
  840. throw new CannotCompileException(e);
  841. }
  842. }
  843. private int insertAfterAdvice(Bytecode code, Javac jv, String src,
  844. ConstPool cp, CtClass rtype, int varNo)
  845. throws CompileError
  846. {
  847. int pc = code.currentPc();
  848. if (rtype == CtClass.voidType) {
  849. code.addOpcode(Opcode.ACONST_NULL);
  850. code.addAstore(varNo);
  851. jv.compileStmnt(src);
  852. code.addOpcode(Opcode.RETURN);
  853. if (code.getMaxLocals() < 1)
  854. code.setMaxLocals(1);
  855. }
  856. else {
  857. code.addStore(varNo, rtype);
  858. jv.compileStmnt(src);
  859. code.addLoad(varNo, rtype);
  860. if (rtype.isPrimitive())
  861. code.addOpcode(((CtPrimitiveType)rtype).getReturnOp());
  862. else
  863. code.addOpcode(Opcode.ARETURN);
  864. }
  865. return code.currentPc() - pc;
  866. }
  867. /*
  868. * assert subr > pos
  869. */
  870. private void insertGoto(CodeIterator iterator, int subr, int pos)
  871. throws BadBytecode
  872. {
  873. iterator.setMark(subr);
  874. // the gap length might be a multiple of 4.
  875. iterator.writeByte(Opcode.NOP, pos);
  876. boolean wide = subr + 2 - pos > Short.MAX_VALUE;
  877. int len = wide ? 4 : 2;
  878. CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false);
  879. pos = gap.position + gap.length - len;
  880. int offset = iterator.getMark() - pos;
  881. if (wide) {
  882. iterator.writeByte(Opcode.GOTO_W, pos);
  883. iterator.write32bit(offset, pos + 1);
  884. }
  885. else if (offset <= Short.MAX_VALUE) {
  886. iterator.writeByte(Opcode.GOTO, pos);
  887. iterator.write16bit(offset, pos + 1);
  888. }
  889. else {
  890. if (gap.length < 4) {
  891. CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false);
  892. pos = gap2.position + gap2.length + gap.length - 4;
  893. }
  894. iterator.writeByte(Opcode.GOTO_W, pos);
  895. iterator.write32bit(iterator.getMark() - pos, pos + 1);
  896. }
  897. }
  898. /* insert a finally clause
  899. */
  900. private int insertAfterHandler(boolean asFinally, Bytecode b,
  901. CtClass rtype, int returnVarNo,
  902. Javac javac, String src)
  903. throws CompileError
  904. {
  905. if (!asFinally)
  906. return 0;
  907. int var = b.getMaxLocals();
  908. b.incMaxLocals(1);
  909. int pc = b.currentPc();
  910. b.addAstore(var); // store an exception
  911. if (rtype.isPrimitive()) {
  912. char c = ((CtPrimitiveType)rtype).getDescriptor();
  913. if (c == 'D') {
  914. b.addDconst(0.0);
  915. b.addDstore(returnVarNo);
  916. }
  917. else if (c == 'F') {
  918. b.addFconst(0);
  919. b.addFstore(returnVarNo);
  920. }
  921. else if (c == 'J') {
  922. b.addLconst(0);
  923. b.addLstore(returnVarNo);
  924. }
  925. else if (c == 'V') {
  926. b.addOpcode(Opcode.ACONST_NULL);
  927. b.addAstore(returnVarNo);
  928. }
  929. else { // int, boolean, char, short, ...
  930. b.addIconst(0);
  931. b.addIstore(returnVarNo);
  932. }
  933. }
  934. else {
  935. b.addOpcode(Opcode.ACONST_NULL);
  936. b.addAstore(returnVarNo);
  937. }
  938. javac.compileStmnt(src);
  939. b.addAload(var);
  940. b.addOpcode(Opcode.ATHROW);
  941. return b.currentPc() - pc;
  942. }
  943. /* -- OLD version --
  944. public void insertAfter(String src) throws CannotCompileException {
  945. declaringClass.checkModify();
  946. CodeAttribute ca = methodInfo.getCodeAttribute();
  947. CodeIterator iterator = ca.iterator();
  948. Bytecode b = new Bytecode(methodInfo.getConstPool(),
  949. ca.getMaxStack(), ca.getMaxLocals());
  950. b.setStackDepth(ca.getMaxStack());
  951. Javac jv = new Javac(b, declaringClass);
  952. try {
  953. jv.recordParams(getParameterTypes(),
  954. Modifier.isStatic(getModifiers()));
  955. CtClass rtype = getReturnType0();
  956. int varNo = jv.recordReturnType(rtype, true);
  957. boolean isVoid = rtype == CtClass.voidType;
  958. if (isVoid) {
  959. b.addOpcode(Opcode.ACONST_NULL);
  960. b.addAstore(varNo);
  961. jv.compileStmnt(src);
  962. }
  963. else {
  964. b.addStore(varNo, rtype);
  965. jv.compileStmnt(src);
  966. b.addLoad(varNo, rtype);
  967. }
  968. byte[] code = b.get();
  969. ca.setMaxStack(b.getMaxStack());
  970. ca.setMaxLocals(b.getMaxLocals());
  971. while (iterator.hasNext()) {
  972. int pos = iterator.next();
  973. int c = iterator.byteAt(pos);
  974. if (c == Opcode.ARETURN || c == Opcode.IRETURN
  975. || c == Opcode.FRETURN || c == Opcode.LRETURN
  976. || c == Opcode.DRETURN || c == Opcode.RETURN)
  977. iterator.insert(pos, code);
  978. }
  979. }
  980. catch (NotFoundException e) {
  981. throw new CannotCompileException(e);
  982. }
  983. catch (CompileError e) {
  984. throw new CannotCompileException(e);
  985. }
  986. catch (BadBytecode e) {
  987. throw new CannotCompileException(e);
  988. }
  989. }
  990. */
  991. /**
  992. * Adds a catch clause that handles an exception thrown in the
  993. * body. The catch clause must end with a return or throw statement.
  994. *
  995. * @param src the source code representing the catch clause.
  996. * It must be a single statement or block.
  997. * @param exceptionType the type of the exception handled by the
  998. * catch clause.
  999. */
  1000. public void addCatch(String src, CtClass exceptionType)
  1001. throws CannotCompileException
  1002. {
  1003. addCatch(src, exceptionType, "$e");
  1004. }
  1005. /**
  1006. * Adds a catch clause that handles an exception thrown in the
  1007. * body. The catch clause must end with a return or throw statement.
  1008. *
  1009. * @param src the source code representing the catch clause.
  1010. * It must be a single statement or block.
  1011. * @param exceptionType the type of the exception handled by the
  1012. * catch clause.
  1013. * @param exceptionName the name of the variable containing the
  1014. * caught exception, for example,
  1015. * <code>$e</code>.
  1016. */
  1017. public void addCatch(String src, CtClass exceptionType,
  1018. String exceptionName)
  1019. throws CannotCompileException
  1020. {
  1021. CtClass cc = declaringClass;
  1022. cc.checkModify();
  1023. ConstPool cp = methodInfo.getConstPool();
  1024. CodeAttribute ca = methodInfo.getCodeAttribute();
  1025. CodeIterator iterator = ca.iterator();
  1026. Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
  1027. b.setStackDepth(1);
  1028. Javac jv = new Javac(b, cc);
  1029. try {
  1030. jv.recordParams(getParameterTypes(),
  1031. Modifier.isStatic(getModifiers()));
  1032. int var = jv.recordVariable(exceptionType, exceptionName);
  1033. b.addAstore(var);
  1034. jv.compileStmnt(src);
  1035. int stack = b.getMaxStack();
  1036. int locals = b.getMaxLocals();
  1037. if (stack > ca.getMaxStack())
  1038. ca.setMaxStack(stack);
  1039. if (locals > ca.getMaxLocals())
  1040. ca.setMaxLocals(locals);
  1041. int len = iterator.getCodeLength();
  1042. int pos = iterator.append(b.get());
  1043. ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
  1044. cp.addClassInfo(exceptionType));
  1045. iterator.append(b.getExceptionTable(), pos);
  1046. methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
  1047. }
  1048. catch (NotFoundException e) {
  1049. throw new CannotCompileException(e);
  1050. }
  1051. catch (CompileError e) {
  1052. throw new CannotCompileException(e);
  1053. } catch (BadBytecode e) {
  1054. throw new CannotCompileException(e);
  1055. }
  1056. }
  1057. /* CtConstructor overrides this method.
  1058. */
  1059. int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
  1060. return 0;
  1061. }
  1062. /**
  1063. * Inserts bytecode at the specified line in the body.
  1064. * It is equivalent to:
  1065. *
  1066. * <br><code>insertAt(lineNum, true, src)</code>
  1067. *
  1068. * <br>See this method as well.
  1069. *
  1070. * @param lineNum the line number. The bytecode is inserted at the
  1071. * beginning of the code at the line specified by this
  1072. * line number.
  1073. * @param src the source code representing the inserted bytecode.
  1074. * It must be a single statement or block.
  1075. * @return the line number at which the bytecode has been inserted.
  1076. *
  1077. * @see CtBehavior#insertAt(int,boolean,String)
  1078. */
  1079. public int insertAt(int lineNum, String src)
  1080. throws CannotCompileException
  1081. {
  1082. return insertAt(lineNum, true, src);
  1083. }
  1084. /**
  1085. * Inserts bytecode at the specified line in the body.
  1086. *
  1087. * <p>If there is not
  1088. * a statement at the specified line, the bytecode might be inserted
  1089. * at the line including the first statement after that line specified.
  1090. * For example, if there is only a closing brace at that line, the
  1091. * bytecode would be inserted at another line below.
  1092. * To know exactly where the bytecode will be inserted, call with
  1093. * <code>modify</code> set to <code>false</code>.
  1094. *
  1095. * @param lineNum the line number. The bytecode is inserted at the
  1096. * beginning of the code at the line specified by this
  1097. * line number.
  1098. * @param modify if false, this method does not insert the bytecode.
  1099. * It instead only returns the line number at which
  1100. * the bytecode would be inserted.
  1101. * @param src the source code representing the inserted bytecode.
  1102. * It must be a single statement or block.
  1103. * If modify is false, the value of src can be null.
  1104. * @return the line number at which the bytecode has been inserted.
  1105. */
  1106. public int insertAt(int lineNum, boolean modify, String src)
  1107. throws CannotCompileException
  1108. {
  1109. CodeAttribute ca = methodInfo.getCodeAttribute();
  1110. if (ca == null)
  1111. throw new CannotCompileException("no method body");
  1112. LineNumberAttribute ainfo
  1113. = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
  1114. if (ainfo == null)
  1115. throw new CannotCompileException("no line number info");
  1116. LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
  1117. lineNum = pc.line;
  1118. int index = pc.index;
  1119. if (!modify)
  1120. return lineNum;
  1121. CtClass cc = declaringClass;
  1122. cc.checkModify();
  1123. CodeIterator iterator = ca.iterator();
  1124. Javac jv = new Javac(cc);
  1125. try {
  1126. jv.recordLocalVariables(ca, index);
  1127. jv.recordParams(getParameterTypes(),
  1128. Modifier.isStatic(getModifiers()));
  1129. jv.setMaxLocals(ca.getMaxLocals());
  1130. jv.compileStmnt(src);
  1131. Bytecode b = jv.getBytecode();
  1132. int locals = b.getMaxLocals();
  1133. int stack = b.getMaxStack();
  1134. ca.setMaxLocals(locals);
  1135. /* We assume that there is no values in the operand stack
  1136. * at the position where the bytecode is inserted.
  1137. */
  1138. if (stack > ca.getMaxStack())
  1139. ca.setMaxStack(stack);
  1140. index = iterator.insertAt(index, b.get());
  1141. iterator.insert(b.getExceptionTable(), index);
  1142. methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
  1143. return lineNum;
  1144. }
  1145. catch (NotFoundException e) {
  1146. throw new CannotCompileException(e);
  1147. }
  1148. catch (CompileError e) {
  1149. throw new CannotCompileException(e);
  1150. }
  1151. catch (BadBytecode e) {
  1152. throw new CannotCompileException(e);
  1153. }
  1154. }
  1155. }