blob: 333d0f90d9cb8325154d487403ff06360c06974e [file] [log] [blame]
package org.apache.maven.shared.dependency.analyzer.asm;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A small parser to read the constant pool directly, in case it contains references
* ASM does not support.
*
* Adapted from http://stackoverflow.com/a/32278587/23691
*
* Constant pool types:
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVM 9 Sepc</a>
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html#jvms-4.4">JVM 10 Sepc</a>
*
*/
public class ConstantPoolParser
{
public static final int HEAD = 0xcafebabe;
// Constant pool types
public static final byte CONSTANT_UTF8 = 1;
public static final byte CONSTANT_INTEGER = 3;
public static final byte CONSTANT_FLOAT = 4;
public static final byte CONSTANT_LONG = 5;
public static final byte CONSTANT_DOUBLE = 6;
public static final byte CONSTANT_CLASS = 7;
public static final byte CONSTANT_STRING = 8;
public static final byte CONSTANT_FIELDREF = 9;
public static final byte CONSTANT_METHODREF = 10;
public static final byte CONSTANT_INTERFACEMETHODREF = 11;
public static final byte CONSTANT_NAME_AND_TYPE = 12;
public static final byte CONSTANT_METHODHANDLE = 15;
public static final byte CONSTANT_METHOD_TYPE = 16;
public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
public static final byte CONSTANT_MODULE = 19;
public static final byte CONSTANT_PACKAGE = 20;
private static final int OXF0 = 0xf0;
private static final int OXE0 = 0xe0;
private static final int OX3F = 0x3F;
static Set<String> getConstantPoolClassReferences( byte[] b )
{
return parseConstantPoolClassReferences( ByteBuffer.wrap( b ) );
}
static Set<String> parseConstantPoolClassReferences( ByteBuffer buf )
{
if ( buf.order( ByteOrder.BIG_ENDIAN )
.getInt() != HEAD )
{
return Collections.emptySet();
}
buf.getChar() ; buf.getChar(); // minor + ver
Set<Integer> classes = new HashSet<Integer>();
Map<Integer, String> stringConstants = new HashMap<Integer, String>();
for ( int ix = 1, num = buf.getChar(); ix < num; ix++ )
{
byte tag = buf.get();
switch ( tag )
{
default:
throw new RuntimeException( "Unknown constant pool type '" + tag + "'" );
case CONSTANT_UTF8:
stringConstants.put( ix, decodeString( buf ) );
continue;
case CONSTANT_CLASS:
case CONSTANT_STRING:
case CONSTANT_METHOD_TYPE:
classes.add( (int) buf.getChar() );
break;
case CONSTANT_FIELDREF:
case CONSTANT_METHODREF:
case CONSTANT_INTERFACEMETHODREF:
case CONSTANT_NAME_AND_TYPE:
buf.getChar();
buf.getChar();
break;
case CONSTANT_INTEGER:
buf.getInt();
break;
case CONSTANT_FLOAT:
buf.getFloat();
break;
case CONSTANT_DOUBLE:
buf.getDouble();
ix++;
break;
case CONSTANT_LONG:
buf.getLong();
ix++;
break;
case CONSTANT_METHODHANDLE:
buf.get();
buf.getChar();
break;
case CONSTANT_INVOKE_DYNAMIC:
buf.getChar();
buf.getChar();
break;
case CONSTANT_MODULE:
buf.getChar();
break;
case CONSTANT_PACKAGE:
buf.getChar();
break;
}
}
Set<String> result = new HashSet<String>();
for ( Integer aClass : classes )
{
result.add( stringConstants.get( aClass ) );
}
return result;
}
private static String decodeString( ByteBuffer buf )
{
int size = buf.getChar(), oldLimit = buf.limit();
buf.limit( buf.position() + size );
StringBuilder sb = new StringBuilder( size + ( size >> 1 ) + 16 );
while ( buf.hasRemaining() )
{
byte b = buf.get();
if ( b > 0 )
{
sb.append( (char) b );
}
else
{
int b2 = buf.get();
if ( ( b & OXF0 ) != OXE0 )
{
sb.append( (char) ( ( b & 0x1F ) << 6 | b2 & OX3F ) );
}
else
{
int b3 = buf.get();
sb.append( (char) ( ( b & 0x0F ) << 12 | ( b2 & OX3F ) << 6 | b3 & OX3F ) );
}
}
}
buf.limit( oldLimit );
return sb.toString();
}
}