1 /* 2 * Copyright (c) 2011, 2018, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.graphio; 26 27 import java.io.Closeable; 28 import java.io.IOException; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.nio.ByteBuffer; 32 import java.nio.channels.WritableByteChannel; 33 import java.nio.charset.Charset; 34 import java.util.Collection; 35 import java.util.HashMap; 36 import java.util.Iterator; 37 import java.util.LinkedHashMap; 38 import java.util.LinkedList; 39 import java.util.Map; 40 import java.util.Objects; 41 42 abstract class GraphProtocol<Graph, Node, NodeClass, Edges, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition, Location> implements Closeable { 43 private static final Charset UTF8 = Charset.forName("UTF-8"); 44 45 private static final int CONSTANT_POOL_MAX_SIZE = 8000; 46 47 private static final int BEGIN_GROUP = 0x00; 48 private static final int BEGIN_GRAPH = 0x01; 49 private static final int CLOSE_GROUP = 0x02; 50 51 private static final int POOL_NEW = 0x00; 52 private static final int POOL_STRING = 0x01; 53 private static final int POOL_ENUM = 0x02; 54 private static final int POOL_CLASS = 0x03; 55 private static final int POOL_METHOD = 0x04; 56 private static final int POOL_NULL = 0x05; 57 private static final int POOL_NODE_CLASS = 0x06; 58 private static final int POOL_FIELD = 0x07; 59 private static final int POOL_SIGNATURE = 0x08; 60 private static final int POOL_NODE_SOURCE_POSITION = 0x09; 61 private static final int POOL_NODE = 0x0a; 62 63 private static final int PROPERTY_POOL = 0x00; 64 private static final int PROPERTY_INT = 0x01; 65 private static final int PROPERTY_LONG = 0x02; 66 private static final int PROPERTY_DOUBLE = 0x03; 67 private static final int PROPERTY_FLOAT = 0x04; 68 private static final int PROPERTY_TRUE = 0x05; 69 private static final int PROPERTY_FALSE = 0x06; 70 private static final int PROPERTY_ARRAY = 0x07; 71 private static final int PROPERTY_SUBGRAPH = 0x08; 72 73 private static final int KLASS = 0x00; 74 private static final int ENUM_KLASS = 0x01; 75 76 private static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'}; 77 78 private final ConstantPool constantPool; 79 private final ByteBuffer buffer; 80 private final WritableByteChannel channel; 81 private final boolean embedded; 82 final int versionMajor; 83 final int versionMinor; 84 private boolean printing; 85 86 GraphProtocol(WritableByteChannel channel, int major, int minor, boolean embedded) throws IOException { 87 if (major > 6 || (major == 6 && minor > 0)) { 88 throw new IllegalArgumentException("Unrecognized version " + major + "." + minor); 89 } 90 this.versionMajor = major; 91 this.versionMinor = minor; 92 this.constantPool = new ConstantPool(); 93 this.buffer = ByteBuffer.allocateDirect(256 * 1024); 94 this.channel = channel; 95 this.embedded = embedded; 96 if (!embedded) { 97 writeVersion(); 98 flushEmbedded(); 99 } 100 } 101 102 GraphProtocol(GraphProtocol<?, ?, ?, ?, ?, ?, ?, ?, ?, ?> parent) { 103 this.versionMajor = parent.versionMajor; 104 this.versionMinor = parent.versionMinor; 105 this.constantPool = parent.constantPool; 106 this.buffer = parent.buffer; 107 this.channel = parent.channel; 108 this.embedded = parent.embedded; 109 } 110 111 @SuppressWarnings("all") 112 public final void print(Graph graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException { 113 printing = true; 114 try { 115 writeByte(BEGIN_GRAPH); 116 if (versionMajor >= 3) { 117 writeInt(id); 118 writeString(format); 119 writeInt(args.length); 120 for (Object a : args) { 121 writePropertyObject(graph, a); 122 } 123 } else { 124 writePoolObject(formatTitle(graph, id, format, args)); 125 } 126 writeGraph(graph, properties); 127 flushEmbedded(); 128 flush(); 129 } finally { 130 printing = false; 131 } 132 } 133 134 public final void beginGroup(Graph noGraph, String name, String shortName, ResolvedJavaMethod method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException { 135 printing = true; 136 try { 137 writeByte(BEGIN_GROUP); 138 writePoolObject(name); 139 writePoolObject(shortName); 140 writePoolObject(method); 141 writeInt(bci); 142 writeProperties(noGraph, properties); 143 flushEmbedded(); 144 } finally { 145 printing = false; 146 } 147 } 148 149 public final void endGroup() throws IOException { 150 printing = true; 151 try { 152 writeByte(CLOSE_GROUP); 153 flushEmbedded(); 154 } finally { 155 printing = false; 156 } 157 } 158 159 final int write(ByteBuffer src) throws IOException { 160 if (printing) { 161 throw new IllegalStateException("Trying to write during graph print."); 162 } 163 constantPool.reset(); 164 return writeBytesRaw(src); 165 } 166 167 final boolean isOpen() { 168 return channel.isOpen(); 169 } 170 171 @Override 172 public final void close() { 173 try { 174 flush(); 175 channel.close(); 176 } catch (IOException ex) { 177 throw new Error(ex); 178 } 179 } 180 181 protected abstract Graph findGraph(Graph current, Object obj); 182 183 protected abstract ResolvedJavaMethod findMethod(Object obj); 184 185 /** 186 * Attempts to recognize the provided object as a node. Used to encode it with 187 * {@link #POOL_NODE} pool type. 188 * 189 * @param obj any object 190 * @return <code>null</code> if it is not a node object, non-null otherwise 191 */ 192 protected abstract Node findNode(Object obj); 193 194 /** 195 * Determines whether the provided object is node class or not. 196 * 197 * @param obj object to check 198 * @return {@code null} if {@code obj} does not represent a NodeClass otherwise the NodeClass 199 * represented by {@code obj} 200 */ 201 protected abstract NodeClass findNodeClass(Object obj); 202 203 /** 204 * Returns the NodeClass for a given Node {@code obj}. 205 * 206 * @param obj instance of node 207 * @return non-{@code null} instance of the node's class object 208 */ 209 protected abstract NodeClass findClassForNode(Node obj); 210 211 /** 212 * Find a Java class. The returned object must be acceptable by 213 * {@link #findJavaTypeName(java.lang.Object)} and return valid name for the class. 214 * 215 * @param clazz node class object 216 * @return object representing the class, for example {@link Class} 217 */ 218 protected abstract Object findJavaClass(NodeClass clazz); 219 220 protected abstract Object findEnumClass(Object enumValue); 221 222 protected abstract String findNameTemplate(NodeClass clazz); 223 224 protected abstract Edges findClassEdges(NodeClass nodeClass, boolean dumpInputs); 225 226 protected abstract int findNodeId(Node n); 227 228 protected abstract void findExtraNodes(Node node, Collection<? super Node> extraNodes); 229 230 protected abstract boolean hasPredecessor(Node node); 231 232 protected abstract int findNodesCount(Graph info); 233 234 protected abstract Iterable<? extends Node> findNodes(Graph info); 235 236 protected abstract void findNodeProperties(Node node, Map<String, Object> props, Graph info); 237 238 protected abstract Collection<? extends Node> findBlockNodes(Graph info, Block block); 239 240 protected abstract int findBlockId(Block sux); 241 242 protected abstract Collection<? extends Block> findBlocks(Graph graph); 243 244 protected abstract Collection<? extends Block> findBlockSuccessors(Block block); 245 246 protected abstract String formatTitle(Graph graph, int id, String format, Object... args); 247 248 protected abstract int findSize(Edges edges); 249 250 protected abstract boolean isDirect(Edges edges, int i); 251 252 protected abstract String findName(Edges edges, int i); 253 254 protected abstract Object findType(Edges edges, int i); 255 256 protected abstract Collection<? extends Node> findNodes(Graph graph, Node node, Edges edges, int i); 257 258 protected abstract int findEnumOrdinal(Object obj); 259 260 protected abstract String[] findEnumTypeValues(Object clazz); 261 262 protected abstract String findJavaTypeName(Object obj); 263 264 protected abstract byte[] findMethodCode(ResolvedJavaMethod method); 265 266 protected abstract int findMethodModifiers(ResolvedJavaMethod method); 267 268 protected abstract Signature findMethodSignature(ResolvedJavaMethod method); 269 270 protected abstract String findMethodName(ResolvedJavaMethod method); 271 272 protected abstract Object findMethodDeclaringClass(ResolvedJavaMethod method); 273 274 protected abstract int findFieldModifiers(ResolvedJavaField field); 275 276 protected abstract String findFieldTypeName(ResolvedJavaField field); 277 278 protected abstract String findFieldName(ResolvedJavaField field); 279 280 protected abstract Object findFieldDeclaringClass(ResolvedJavaField field); 281 282 protected abstract ResolvedJavaField findJavaField(Object object); 283 284 protected abstract Signature findSignature(Object object); 285 286 protected abstract int findSignatureParameterCount(Signature signature); 287 288 protected abstract String findSignatureParameterTypeName(Signature signature, int index); 289 290 protected abstract String findSignatureReturnTypeName(Signature signature); 291 292 protected abstract NodeSourcePosition findNodeSourcePosition(Object object); 293 294 protected abstract ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos); 295 296 protected abstract NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos); 297 298 protected abstract int findNodeSourcePositionBCI(NodeSourcePosition pos); 299 300 protected abstract Iterable<Location> findLocation(ResolvedJavaMethod method, int bci, NodeSourcePosition pos); 301 302 protected abstract String findLocationFile(Location loc) throws IOException; 303 304 protected abstract int findLocationLine(Location loc); 305 306 protected abstract URI findLocationURI(Location loc) throws URISyntaxException; 307 308 protected abstract String findLocationLanguage(Location loc); 309 310 protected abstract int findLocationStart(Location loc); 311 312 protected abstract int findLocationEnd(Location loc); 313 314 private void writeVersion() throws IOException { 315 writeBytesRaw(MAGIC_BYTES); 316 writeByte(versionMajor); 317 writeByte(versionMinor); 318 } 319 320 private void flushEmbedded() throws IOException { 321 if (embedded) { 322 flush(); 323 constantPool.reset(); 324 } 325 } 326 327 private void flush() throws IOException { 328 buffer.flip(); 329 /* 330 * Try not to let interrupted threads abort the write. There's still a race here but an 331 * interrupt that's been pending for a long time shouldn't stop this writing. 332 */ 333 boolean interrupted = Thread.interrupted(); 334 try { 335 channel.write(buffer); 336 } finally { 337 if (interrupted) { 338 Thread.currentThread().interrupt(); 339 } 340 } 341 buffer.compact(); 342 } 343 344 private void ensureAvailable(int i) throws IOException { 345 assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small"; 346 while (buffer.remaining() < i) { 347 flush(); 348 } 349 } 350 351 private void writeByte(int b) throws IOException { 352 ensureAvailable(1); 353 buffer.put((byte) b); 354 } 355 356 private void writeInt(int b) throws IOException { 357 ensureAvailable(4); 358 buffer.putInt(b); 359 } 360 361 private void writeLong(long b) throws IOException { 362 ensureAvailable(8); 363 buffer.putLong(b); 364 } 365 366 private void writeDouble(double b) throws IOException { 367 ensureAvailable(8); 368 buffer.putDouble(b); 369 } 370 371 private void writeFloat(float b) throws IOException { 372 ensureAvailable(4); 373 buffer.putFloat(b); 374 } 375 376 private void writeShort(char b) throws IOException { 377 ensureAvailable(2); 378 buffer.putChar(b); 379 } 380 381 private void writeString(String str) throws IOException { 382 byte[] bytes = str.getBytes(UTF8); 383 writeBytes(bytes); 384 } 385 386 private void writeBytes(byte[] b) throws IOException { 387 if (b == null) { 388 writeInt(-1); 389 } else { 390 writeInt(b.length); 391 writeBytesRaw(b); 392 } 393 } 394 395 private void writeBytesRaw(byte[] b) throws IOException { 396 int bytesWritten = 0; 397 while (bytesWritten < b.length) { 398 int toWrite = Math.min(b.length - bytesWritten, buffer.capacity()); 399 ensureAvailable(toWrite); 400 buffer.put(b, bytesWritten, toWrite); 401 bytesWritten += toWrite; 402 } 403 } 404 405 private int writeBytesRaw(ByteBuffer b) throws IOException { 406 int limit = b.limit(); 407 int written = 0; 408 while (b.position() < limit) { 409 int toWrite = Math.min(limit - b.position(), buffer.capacity()); 410 ensureAvailable(toWrite); 411 b.limit(b.position() + toWrite); 412 try { 413 buffer.put(b); 414 written += toWrite; 415 } finally { 416 b.limit(limit); 417 } 418 } 419 return written; 420 } 421 422 private void writeInts(int[] b) throws IOException { 423 if (b == null) { 424 writeInt(-1); 425 } else { 426 writeInt(b.length); 427 int sizeInBytes = b.length * 4; 428 ensureAvailable(sizeInBytes); 429 buffer.asIntBuffer().put(b); 430 buffer.position(buffer.position() + sizeInBytes); 431 } 432 } 433 434 private void writeDoubles(double[] b) throws IOException { 435 if (b == null) { 436 writeInt(-1); 437 } else { 438 writeInt(b.length); 439 int sizeInBytes = b.length * 8; 440 ensureAvailable(sizeInBytes); 441 buffer.asDoubleBuffer().put(b); 442 buffer.position(buffer.position() + sizeInBytes); 443 } 444 } 445 446 private void writePoolObject(Object obj) throws IOException { 447 Object object = obj; 448 if (object == null) { 449 writeByte(POOL_NULL); 450 return; 451 } 452 Character id = constantPool.get(object); 453 if (id == null) { 454 addPoolEntry(object); 455 } else { 456 int type = findPoolType(object, null); 457 writeByte(type); 458 writeShort(id.charValue()); 459 } 460 } 461 462 private int findPoolType(Object obj, Object[] found) throws IOException { 463 Object object = obj; 464 if (object == null) { 465 return POOL_NULL; 466 } 467 if (isFound(findJavaField(object), found)) { 468 return POOL_FIELD; 469 } else if (isFound(findSignature(object), found)) { 470 return POOL_SIGNATURE; 471 } else if (versionMajor >= 4 && isFound(findNodeSourcePosition(object), found)) { 472 return POOL_NODE_SOURCE_POSITION; 473 } else { 474 final Node node = findNode(object); 475 if (versionMajor == 4 && node != null) { 476 object = classForNode(node); 477 } 478 if (isFound(findNodeClass(object), found)) { 479 return POOL_NODE_CLASS; 480 } else if (versionMajor >= 5 && isFound(node, found)) { 481 return POOL_NODE; 482 } else if (isFound(findMethod(object), found)) { 483 return POOL_METHOD; 484 } else if (object instanceof Enum<?>) { 485 if (found != null) { 486 found[0] = ((Enum<?>) object).ordinal(); 487 } 488 return POOL_ENUM; 489 } else { 490 int val = findEnumOrdinal(object); 491 if (val >= 0) { 492 if (found != null) { 493 found[0] = val; 494 } 495 return POOL_ENUM; 496 } else if (object instanceof Class<?>) { 497 if (found != null) { 498 found[0] = ((Class<?>) object).getName(); 499 } 500 return POOL_CLASS; 501 } else if (isFound(findJavaTypeName(object), found)) { 502 return POOL_CLASS; 503 } else { 504 return POOL_STRING; 505 } 506 } 507 } 508 } 509 510 private void writeGraph(Graph graph, Map<? extends Object, ? extends Object> properties) throws IOException { 511 writeProperties(graph, properties); 512 writeNodes(graph); 513 writeBlocks(findBlocks(graph), graph); 514 } 515 516 private void writeNodes(Graph info) throws IOException { 517 Map<String, Object> props = new HashMap<>(); 518 519 final int size = findNodesCount(info); 520 writeInt(size); 521 int cnt = 0; 522 for (Node node : findNodes(info)) { 523 NodeClass nodeClass = classForNode(node); 524 findNodeProperties(node, props, info); 525 526 writeInt(findNodeId(node)); 527 writePoolObject(nodeClass); 528 writeByte(hasPredecessor(node) ? 1 : 0); 529 writeProperties(info, props); 530 writeEdges(info, node, true); 531 writeEdges(info, node, false); 532 533 props.clear(); 534 cnt++; 535 } 536 if (size != cnt) { 537 throw new IOException("Expecting " + size + " nodes, but found " + cnt); 538 } 539 } 540 541 private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException { 542 NodeClass clazz = classForNode(node); 543 Edges edges = findClassEdges(clazz, dumpInputs); 544 int size = findSize(edges); 545 for (int i = 0; i < size; i++) { 546 Collection<? extends Node> list = findNodes(graph, node, edges, i); 547 if (isDirect(edges, i)) { 548 if (list != null && list.size() != 1) { 549 throw new IOException("Edge " + i + " in " + edges + " is direct, but list isn't singleton: " + list); 550 } 551 Node n = null; 552 if (list != null && !list.isEmpty()) { 553 n = list.iterator().next(); 554 } 555 writeNodeRef(n); 556 } else { 557 if (list == null) { 558 writeShort((char) 0); 559 } else { 560 int listSize = list.size(); 561 assert listSize == ((char) listSize); 562 writeShort((char) listSize); 563 for (Node edge : list) { 564 writeNodeRef(edge); 565 } 566 } 567 } 568 } 569 } 570 571 private NodeClass classForNode(Node node) throws IOException { 572 NodeClass clazz = findClassForNode(node); 573 if (clazz == null) { 574 throw new IOException("No class for " + node); 575 } 576 return clazz; 577 } 578 579 private void writeNodeRef(Node node) throws IOException { 580 writeInt(findNodeId(node)); 581 } 582 583 private void writeBlocks(Collection<? extends Block> blocks, Graph info) throws IOException { 584 if (blocks != null) { 585 for (Block block : blocks) { 586 Collection<? extends Node> nodes = findBlockNodes(info, block); 587 if (nodes == null) { 588 writeInt(0); 589 return; 590 } 591 } 592 writeInt(blocks.size()); 593 for (Block block : blocks) { 594 Collection<? extends Node> nodes = findBlockNodes(info, block); 595 writeInt(findBlockId(block)); 596 writeInt(nodes.size()); 597 for (Node node : nodes) { 598 writeInt(findNodeId(node)); 599 } 600 final Collection<? extends Block> successors = findBlockSuccessors(block); 601 writeInt(successors.size()); 602 for (Block sux : successors) { 603 writeInt(findBlockId(sux)); 604 } 605 } 606 } else { 607 writeInt(0); 608 } 609 } 610 611 private void writeEdgesInfo(NodeClass nodeClass, boolean dumpInputs) throws IOException { 612 Edges edges = findClassEdges(nodeClass, dumpInputs); 613 int size = findSize(edges); 614 writeShort((char) size); 615 for (int i = 0; i < size; i++) { 616 writeByte(isDirect(edges, i) ? 0 : 1); 617 writePoolObject(findName(edges, i)); 618 if (dumpInputs) { 619 writePoolObject(findType(edges, i)); 620 } 621 } 622 } 623 624 @SuppressWarnings("unchecked") 625 private void addPoolEntry(Object obj) throws IOException { 626 Object object = obj; 627 char index = constantPool.add(object); 628 writeByte(POOL_NEW); 629 writeShort(index); 630 631 Object[] found = {null}; 632 int type = findPoolType(object, found); 633 writeByte(type); 634 switch (type) { 635 case POOL_FIELD: { 636 ResolvedJavaField field = (ResolvedJavaField) found[0]; 637 Objects.requireNonNull(field); 638 writePoolObject(findFieldDeclaringClass(field)); 639 writePoolObject(findFieldName(field)); 640 writePoolObject(findFieldTypeName(field)); 641 writeInt(findFieldModifiers(field)); 642 break; 643 } 644 case POOL_SIGNATURE: { 645 Signature signature = (Signature) found[0]; 646 int args = findSignatureParameterCount(signature); 647 writeShort((char) args); 648 for (int i = 0; i < args; i++) { 649 writePoolObject(findSignatureParameterTypeName(signature, i)); 650 } 651 writePoolObject(findSignatureReturnTypeName(signature)); 652 break; 653 } 654 case POOL_NODE_SOURCE_POSITION: { 655 NodeSourcePosition pos = (NodeSourcePosition) found[0]; 656 Objects.requireNonNull(pos); 657 ResolvedJavaMethod method = findNodeSourcePositionMethod(pos); 658 writePoolObject(method); 659 final int bci = findNodeSourcePositionBCI(pos); 660 writeInt(bci); 661 Iterator<Location> ste = findLocation(method, bci, pos).iterator(); 662 if (versionMajor >= 6) { 663 while (ste.hasNext()) { 664 Location loc = ste.next(); 665 URI uri; 666 try { 667 uri = findLocationURI(loc); 668 } catch (URISyntaxException ex) { 669 throw new IOException(ex); 670 } 671 if (uri == null) { 672 continue; 673 } 674 String l = findLocationLanguage(loc); 675 if (l == null) { 676 continue; 677 } 678 writePoolObject(uri.toString()); 679 writeString(l); 680 writeInt(findLocationLine(loc)); 681 writeInt(findLocationStart(loc)); 682 writeInt(findLocationEnd(loc)); 683 } 684 writePoolObject(null); 685 } else { 686 Location first = ste.hasNext() ? ste.next() : null; 687 String fileName = first != null ? findLocationFile(first) : null; 688 if (fileName != null) { 689 writePoolObject(fileName); 690 writeInt(findLocationLine(first)); 691 } else { 692 writePoolObject(null); 693 } 694 } 695 writePoolObject(findNodeSourcePositionCaller(pos)); 696 break; 697 } 698 case POOL_NODE: { 699 Node node = (Node) found[0]; 700 Objects.requireNonNull(node); 701 writeInt(findNodeId(node)); 702 writePoolObject(classForNode(node)); 703 break; 704 } 705 case POOL_NODE_CLASS: { 706 NodeClass nodeClass = (NodeClass) found[0]; 707 final Object clazz = findJavaClass(nodeClass); 708 if (versionMajor >= 3) { 709 writePoolObject(clazz); 710 writeString(findNameTemplate(nodeClass)); 711 } else { 712 writeString(((Class<?>) clazz).getSimpleName()); 713 String nameTemplate = findNameTemplate(nodeClass); 714 writeString(nameTemplate); 715 } 716 writeEdgesInfo(nodeClass, true); 717 writeEdgesInfo(nodeClass, false); 718 break; 719 } 720 case POOL_CLASS: { 721 String typeName = (String) found[0]; 722 Objects.requireNonNull(typeName); 723 writeString(typeName); 724 String[] enumValueNames = findEnumTypeValues(object); 725 if (enumValueNames != null) { 726 writeByte(ENUM_KLASS); 727 writeInt(enumValueNames.length); 728 for (String o : enumValueNames) { 729 writePoolObject(o); 730 } 731 } else { 732 writeByte(KLASS); 733 } 734 break; 735 } 736 case POOL_METHOD: { 737 ResolvedJavaMethod method = (ResolvedJavaMethod) found[0]; 738 Objects.requireNonNull(method); 739 writePoolObject(findMethodDeclaringClass(method)); 740 writePoolObject(findMethodName(method)); 741 final Signature methodSignature = findMethodSignature(method); 742 if (findSignature(methodSignature) == null) { 743 throw new IOException("Should be recognized as signature: " + methodSignature + " for " + method); 744 } 745 writePoolObject(methodSignature); 746 writeInt(findMethodModifiers(method)); 747 writeBytes(findMethodCode(method)); 748 break; 749 } 750 case POOL_ENUM: { 751 int enumOrdinal = (int) found[0]; 752 writePoolObject(findEnumClass(object)); 753 writeInt(enumOrdinal); 754 break; 755 } 756 case POOL_STRING: { 757 writeString(object.toString()); 758 break; 759 } 760 default: 761 throw new IllegalStateException(); 762 } 763 } 764 765 private void writePropertyObject(Graph graph, Object obj) throws IOException { 766 if (obj instanceof Integer) { 767 writeByte(PROPERTY_INT); 768 writeInt(((Integer) obj).intValue()); 769 } else if (obj instanceof Long) { 770 writeByte(PROPERTY_LONG); 771 writeLong(((Long) obj).longValue()); 772 } else if (obj instanceof Double) { 773 writeByte(PROPERTY_DOUBLE); 774 writeDouble(((Double) obj).doubleValue()); 775 } else if (obj instanceof Float) { 776 writeByte(PROPERTY_FLOAT); 777 writeFloat(((Float) obj).floatValue()); 778 } else if (obj instanceof Boolean) { 779 if (((Boolean) obj).booleanValue()) { 780 writeByte(PROPERTY_TRUE); 781 } else { 782 writeByte(PROPERTY_FALSE); 783 } 784 } else if (obj != null && obj.getClass().isArray()) { 785 Class<?> componentType = obj.getClass().getComponentType(); 786 if (componentType.isPrimitive()) { 787 if (componentType == Double.TYPE) { 788 writeByte(PROPERTY_ARRAY); 789 writeByte(PROPERTY_DOUBLE); 790 writeDoubles((double[]) obj); 791 } else if (componentType == Integer.TYPE) { 792 writeByte(PROPERTY_ARRAY); 793 writeByte(PROPERTY_INT); 794 writeInts((int[]) obj); 795 } else { 796 writeByte(PROPERTY_POOL); 797 writePoolObject(obj); 798 } 799 } else { 800 writeByte(PROPERTY_ARRAY); 801 writeByte(PROPERTY_POOL); 802 Object[] array = (Object[]) obj; 803 writeInt(array.length); 804 for (Object o : array) { 805 writePoolObject(o); 806 } 807 } 808 } else { 809 Graph g = findGraph(graph, obj); 810 if (g == null) { 811 writeByte(PROPERTY_POOL); 812 writePoolObject(obj); 813 } else { 814 writeByte(PROPERTY_SUBGRAPH); 815 writeGraph(g, null); 816 } 817 } 818 } 819 820 private void writeProperties(Graph graph, Map<? extends Object, ? extends Object> props) throws IOException { 821 if (props == null) { 822 writeShort((char) 0); 823 return; 824 } 825 final int size = props.size(); 826 // properties 827 writeShort((char) size); 828 int cnt = 0; 829 for (Map.Entry<? extends Object, ? extends Object> entry : props.entrySet()) { 830 String key = entry.getKey().toString(); 831 writePoolObject(key); 832 writePropertyObject(graph, entry.getValue()); 833 cnt++; 834 } 835 if (size != cnt) { 836 throw new IOException("Expecting " + size + " properties, but found only " + cnt); 837 } 838 } 839 840 private static boolean isFound(Object obj, Object[] found) { 841 if (obj == null) { 842 return false; 843 } 844 if (found != null) { 845 found[0] = obj; 846 } 847 return true; 848 } 849 850 private static final class ConstantPool extends LinkedHashMap<Object, Character> { 851 852 private final LinkedList<Character> availableIds; 853 private char nextId; 854 private static final long serialVersionUID = -2676889957907285681L; 855 856 ConstantPool() { 857 super(50, 0.65f); 858 availableIds = new LinkedList<>(); 859 } 860 861 @Override 862 protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) { 863 if (size() > CONSTANT_POOL_MAX_SIZE) { 864 availableIds.addFirst(eldest.getValue()); 865 return true; 866 } 867 return false; 868 } 869 870 private Character nextAvailableId() { 871 if (!availableIds.isEmpty()) { 872 return availableIds.removeFirst(); 873 } 874 return nextId++; 875 } 876 877 public char add(Object obj) { 878 Character id = nextAvailableId(); 879 put(obj, id); 880 return id; 881 } 882 883 void reset() { 884 clear(); 885 availableIds.clear(); 886 nextId = 0; 887 } 888 } 889 890 }