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