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