Copy classlib to federated build.

git-svn-id: https://svn.apache.org/repos/asf/harmony/enhanced/trunk@926318 13f79535-47bb-0310-9956-ffa450edef68
master
Mark Hindess 14 years ago
commit 57d03126b8
  1. 10
      .classpath
  2. 28
      .project
  3. 12
      .settings/org.eclipse.jdt.core.prefs
  4. 50
      .settings/org.eclipse.jdt.ui.prefs
  5. 25
      META-INF/MANIFEST.MF
  6. 5
      README.txt
  7. 200
      build.xml
  8. 10
      depends/manifests/asm-3.1/META-INF/MANIFEST.MF
  9. 21
      make/depends.properties
  10. 0
      make/exclude.common
  11. 0
      make/exclude.linux.x86.drl
  12. 0
      make/exclude.linux.x86.ibm
  13. 0
      make/exclude.linux.x86_64.drl
  14. 0
      make/exclude.linux.x86_64.ibm
  15. 0
      make/exclude.windows.x86.drl
  16. 0
      make/exclude.windows.x86.ibm
  17. 0
      make/exclude.windows.x86_64.drl
  18. 46
      make/findbugs-exclude-filter.xml
  19. 43
      make/run-test.xml
  20. 328
      src/main/java/org/apache/harmony/pack200/Archive.java
  21. 258
      src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
  22. 516
      src/main/java/org/apache/harmony/pack200/BHSDCodec.java
  23. 808
      src/main/java/org/apache/harmony/pack200/BandSet.java
  24. 582
      src/main/java/org/apache/harmony/pack200/BcBands.java
  25. 57
      src/main/java/org/apache/harmony/pack200/CPClass.java
  26. 26
      src/main/java/org/apache/harmony/pack200/CPConstant.java
  27. 37
      src/main/java/org/apache/harmony/pack200/CPDouble.java
  28. 37
      src/main/java/org/apache/harmony/pack200/CPFloat.java
  29. 43
      src/main/java/org/apache/harmony/pack200/CPInt.java
  30. 48
      src/main/java/org/apache/harmony/pack200/CPLong.java
  31. 95
      src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
  32. 73
      src/main/java/org/apache/harmony/pack200/CPNameAndType.java
  33. 86
      src/main/java/org/apache/harmony/pack200/CPSignature.java
  34. 44
      src/main/java/org/apache/harmony/pack200/CPString.java
  35. 42
      src/main/java/org/apache/harmony/pack200/CPUTF8.java
  36. 210
      src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java
  37. 1553
      src/main/java/org/apache/harmony/pack200/ClassBands.java
  38. 246
      src/main/java/org/apache/harmony/pack200/Codec.java
  39. 346
      src/main/java/org/apache/harmony/pack200/CodecEncoding.java
  40. 34
      src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java
  41. 694
      src/main/java/org/apache/harmony/pack200/CpBands.java
  42. 170
      src/main/java/org/apache/harmony/pack200/FileBands.java
  43. 203
      src/main/java/org/apache/harmony/pack200/IcBands.java
  44. 253
      src/main/java/org/apache/harmony/pack200/IntList.java
  45. 375
      src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java
  46. 145
      src/main/java/org/apache/harmony/pack200/NewAttribute.java
  47. 968
      src/main/java/org/apache/harmony/pack200/NewAttributeBands.java
  48. 110
      src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java
  49. 58
      src/main/java/org/apache/harmony/pack200/Pack200Exception.java
  50. 369
      src/main/java/org/apache/harmony/pack200/PackingOptions.java
  51. 250
      src/main/java/org/apache/harmony/pack200/PackingUtils.java
  52. 169
      src/main/java/org/apache/harmony/pack200/PopulationCodec.java
  53. 158
      src/main/java/org/apache/harmony/pack200/RunCodec.java
  54. 676
      src/main/java/org/apache/harmony/pack200/Segment.java
  55. 392
      src/main/java/org/apache/harmony/pack200/SegmentHeader.java
  56. 245
      src/main/java/org/apache/harmony/unpack200/Archive.java
  57. 121
      src/main/java/org/apache/harmony/unpack200/AttrDefinitionBands.java
  58. 286
      src/main/java/org/apache/harmony/unpack200/AttributeLayout.java
  59. 284
      src/main/java/org/apache/harmony/unpack200/AttributeLayoutMap.java
  60. 551
      src/main/java/org/apache/harmony/unpack200/BandSet.java
  61. 606
      src/main/java/org/apache/harmony/unpack200/BcBands.java
  62. 1418
      src/main/java/org/apache/harmony/unpack200/ClassBands.java
  63. 724
      src/main/java/org/apache/harmony/unpack200/CpBands.java
  64. 127
      src/main/java/org/apache/harmony/unpack200/FileBands.java
  65. 26
      src/main/java/org/apache/harmony/unpack200/IMatcher.java
  66. 249
      src/main/java/org/apache/harmony/unpack200/IcBands.java
  67. 370
      src/main/java/org/apache/harmony/unpack200/IcTuple.java
  68. 252
      src/main/java/org/apache/harmony/unpack200/MetadataBandGroup.java
  69. 995
      src/main/java/org/apache/harmony/unpack200/NewAttributeBands.java
  70. 640
      src/main/java/org/apache/harmony/unpack200/Segment.java
  71. 317
      src/main/java/org/apache/harmony/unpack200/SegmentConstantPool.java
  72. 182
      src/main/java/org/apache/harmony/unpack200/SegmentConstantPoolArrayCache.java
  73. 450
      src/main/java/org/apache/harmony/unpack200/SegmentHeader.java
  74. 126
      src/main/java/org/apache/harmony/unpack200/SegmentOptions.java
  75. 164
      src/main/java/org/apache/harmony/unpack200/SegmentUtils.java
  76. 73
      src/main/java/org/apache/harmony/unpack200/bytecode/AnnotationDefaultAttribute.java
  77. 190
      src/main/java/org/apache/harmony/unpack200/bytecode/AnnotationsAttribute.java
  78. 115
      src/main/java/org/apache/harmony/unpack200/bytecode/Attribute.java
  79. 78
      src/main/java/org/apache/harmony/unpack200/bytecode/BCIRenumberedAttribute.java
  80. 383
      src/main/java/org/apache/harmony/unpack200/bytecode/ByteCode.java
  81. 96
      src/main/java/org/apache/harmony/unpack200/bytecode/CPClass.java
  82. 71
      src/main/java/org/apache/harmony/unpack200/bytecode/CPConstant.java
  83. 32
      src/main/java/org/apache/harmony/unpack200/bytecode/CPConstantNumber.java
  84. 39
      src/main/java/org/apache/harmony/unpack200/bytecode/CPDouble.java
  85. 34
      src/main/java/org/apache/harmony/unpack200/bytecode/CPField.java
  86. 98
      src/main/java/org/apache/harmony/unpack200/bytecode/CPFieldRef.java
  87. 39
      src/main/java/org/apache/harmony/unpack200/bytecode/CPFloat.java
  88. 39
      src/main/java/org/apache/harmony/unpack200/bytecode/CPInteger.java
  89. 54
      src/main/java/org/apache/harmony/unpack200/bytecode/CPInterfaceMethodRef.java
  90. 39
      src/main/java/org/apache/harmony/unpack200/bytecode/CPLong.java
  91. 123
      src/main/java/org/apache/harmony/unpack200/bytecode/CPMember.java
  92. 52
      src/main/java/org/apache/harmony/unpack200/bytecode/CPMethod.java
  93. 51
      src/main/java/org/apache/harmony/unpack200/bytecode/CPMethodRef.java
  94. 123
      src/main/java/org/apache/harmony/unpack200/bytecode/CPNameAndType.java
  95. 114
      src/main/java/org/apache/harmony/unpack200/bytecode/CPRef.java
  96. 73
      src/main/java/org/apache/harmony/unpack200/bytecode/CPString.java
  97. 90
      src/main/java/org/apache/harmony/unpack200/bytecode/CPUTF8.java
  98. 277
      src/main/java/org/apache/harmony/unpack200/bytecode/ClassConstantPool.java
  99. 73
      src/main/java/org/apache/harmony/unpack200/bytecode/ClassFile.java
  100. 65
      src/main/java/org/apache/harmony/unpack200/bytecode/ClassFileEntry.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src/main/java"/>
<classpathentry kind="src" output="bin/test" path="src/test/java"/>
<classpathentry kind="src" output="bin/test" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
<classpathentry kind="output" path="bin/main"/>
</classpath>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>pack200</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

@ -0,0 +1,12 @@
#Tue Jan 09 09:46:02 GMT 2007
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.4
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
org.eclipse.jdt.core.compiler.source=1.3

@ -0,0 +1,50 @@
#Sun Feb 11 23:16:33 GMT 2007
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=false
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.format_source_code=true
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=false
sp_cleanup.remove_trailing_whitespaces_ignore_empty=true
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=true
sp_cleanup.remove_unused_imports=false
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=true
sp_cleanup.sort_members_all=true
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true

@ -0,0 +1,25 @@
Manifest-Version: 1.0
Specification-Title: Java Platform API Specification
Specification-Version: 1.5
Implementation-Title: Apache Harmony
Implementation-Vendor: The Apache Software Foundation
Implementation-Vendor-Id: org.apache.harmony
Implementation-URL: http://harmony.apache.org
Bundle-ManifestVersion: 2
Bundle-Name: Harmony Pack200
Bundle-SymbolicName: org.apache.harmony.pack200
Bundle-Version: 1.0.0
Bundle-ClassPath: .
Eclipse-JREBundle: true
Import-Package: java.io;resolution:=optional,
java.lang;resolution:=optional,
java.util;resolution:=optional,
java.util.jar;resolution:=optional,
org.objectweb.asm
Export-Package: org.apache.harmony.pack200,
org.apache.harmony.unpack200,
org.apache.harmony.unpack200.bytecode,
org.apache.harmony.unpack200.bytecode.forms
Bundle-RequiredExecutionEnvironment: J2SE-1.4,
J2SE-1.5

@ -0,0 +1,5 @@
The java5 source directory are files that need to be compiled under Java 5.
Currently, they're not visible in the Eclipse project.
At build time, the contents of the src/main/java5 project are be added to the
build (with a 1.5 target) and the src/main/java should be compiled with a
1.4 target.

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project name="PACK200 Build" default="build" basedir=".">
<description>Build for PACK200 component</description>
<property name="hy.module" value="pack200" />
<property name="hy.hdk" location="${basedir}/../../deploy" />
<property name="depends.dir" location="${basedir}/../../depends" />
<import file="${hy.hdk}/build/ant/properties.xml" />
<import file="${hy.hdk}/build/ant/depends.xml" />
<property file="../../make/depends.properties" />
<target name="build" depends="check-depends, compile-java, build-jar" />
<target name="check-depends">
<check-one-file src="${asm.url}" dest="${asm.jar}" />
<copy todir="${hy.jdk}/jre/lib/boot">
<fileset dir="${depends.jars}">
<patternset includes="${asm.ver}/*.jar" />
</fileset>
<fileset dir="depends/manifests"/>
</copy>
</target>
<target name="fetch-depends">
<mkdir dir="${asm.dir}" />
<download-one-file src="${asm.url}" dest="${asm.jar}"
md5="${asm.md5}" />
</target>
<target name="test" depends="-test-module">
<fail message="Some tests failed">
<condition>
<or>
<isset property="test.failures" />
<isset property="test.errors" />
</or>
</condition>
</fail>
</target>
<!-- internal target for local and global test run sequence -->
<target name="-test-module" depends="build, run-tests" />
<target name="clean" depends="clean-java,clean-test" />
<target name="clean-java" depends="class-patternset">
<delete file="${hy.jdk}/jre/lib/boot/pack200.jar" />
<delete file="${hy.jdk}/jre/lib/boot/pack200-src.jar" />
<delete failonerror="false">
<fileset refid="classes" />
</delete>
<delete-patternset module="${hy.module}" />
</target>
<target name="clean-test">
<delete dir="${tests.hdk.dir}" failonerror="false" />
<delete failonerror="false" dir="bin"/>
</target>
<target name="compile-java">
<echo message="Compiling ${hy.module} classes" />
<mkdir dir="../../build/classes" />
<!-- Ordinary classes -->
<hy.javac sourcepath=""
srcdir="src/main/java"
destdir="../../build/classes"
source="1.4"
target="1.4">
<compilerarg line="${build.compilerarg}" />
<bootclasspath>
<fileset dir="${hy.jdk}/jre/lib/boot">
<include name="**/*.jar" />
</fileset>
</bootclasspath>
</hy.javac>
<!-- Java5 classes, with generics -->
<hy.javac sourcepath=""
srcdir="src/main/java5"
destdir="../../build/classes">
<compilerarg line="${build.compilerarg}" />
<bootclasspath>
<fileset dir="${hy.jdk}/jre/lib/boot">
<include name="**/*.jar" />
</fileset>
</bootclasspath>
</hy.javac>
</target>
<target name="findbugs" depends="build-jar">
<run-findBugs
jarFile="${hy.jdk}/jre/lib/boot/pack200.jar"
excludeFilter="make/findbugs-exclude-filter.xml"
outputFile="${findBugs.report}/pack200.xml"/>
</target>
<target name="build-jar" depends="svn-info,class-patternset">
<hy.jar.bin destfile="${hy.jdk}/jre/lib/boot/pack200.jar"
manifest="META-INF/MANIFEST.MF">
<manifest>
<attribute name="X-Compile-Source-JDK"
value="${hy.javac.source} (parts 1.4)"/>
<attribute name="X-Compile-Target-JDK"
value="${hy.javac.target} (parts 1.4)"/>
</manifest>
<fileset refid="classes" />
<fileset refid="resources" />
</hy.jar.bin>
<hy.jar.src destfile="${hy.jdk}/jre/lib/boot/pack200-src.jar">
<fileset dir="src/main/java" />
<fileset dir="src/main/java/../java5" />
</hy.jar.src>
</target>
<target name="test-jar" depends="svn-info,compile-tests">
<mkdir dir="${tests.hdk.dir}" />
<copy file="make/run-test.xml" tofile="${tests.hdk.dir}/build.xml" />
<hy.jar.bin destfile="${tests.hdk.dir}/pack200_tests.jar">
<fileset dir="bin/test" />
</hy.jar.bin>
<mkdir dir="${tests.excludes.hdk.dir}"/>
<copy todir="${tests.excludes.hdk.dir}">
<fileset dir="make" includes="exclude*"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="\.java" replace="\.class" flags="g"/>
</tokenfilter>
</filterchain>
</copy>
<copy todir="${tests.resources.hdk.dir}">
<fileset dir="src/test/resources" />
</copy>
</target>
<target name="compile-tests">
<mkdir dir="bin/test" />
<mkdir dir="${hy.hdk}/build/test" />
<copy file="${junit.jar}"
tofile="${hy.hdk}/build/test/junit.jar" />
<copy file="${hamcrest-library.jar}"
tofile="${hy.hdk}/build/test/hamcrest-library.jar" />
<echo message="Compiling ${hy.module} tests" />
<hy.javac srcdir="src/test/java"
destdir="bin/test"
sourcepath=""
source="1.4"
target="1.4">
<compilerarg line="${build.compilerarg}" />
<bootclasspath>
<fileset dir="${hy.jdk}/jre/lib/boot">
<include name="**/*.jar" />
</fileset>
</bootclasspath>
<classpath location="${hy.hdk}/build/test/junit.jar" />
<classpath location="${hy.hdk}/build/test/hamcrest-library.jar" />
</hy.javac>
</target>
<target name="run-tests" depends="test-jar">
<ant dir="${tests.hdk.dir}" target="test-module" />
</target>
<target name="class-patternset">
<make-patternset module="${hy.module}" />
</target>
</project>

@ -0,0 +1,10 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: ASM
Bundle-SymbolicName: org.objectweb.asm;singleton:=true
Bundle-Version: 3.1
Bundle-ClassPath: asm-3.1.jar
Eclipse-JREBundle: true
Import-Package: java.io,
java.lang
Export-Package: org.objectweb.asm, org.objectweb.asm.signature

@ -0,0 +1,21 @@
# 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.
depends.jars=${depends.dir}/jars
asm.ver=asm-3.1
asm.dir=${depends.jars}/${asm.ver}
asm.jar=${asm.dir}/${asm.ver}.jar
asm.url=http://download.forge.objectweb.org/asm/${asm.ver}.jar
asm.md5=4fbe0fd975ecc71480846ce272b483b0

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<!-- Exclusion filter for running FindBugs against Apache Harmony -->
<!--
Format and code information is available here
http://findbugs.sourceforge.net/manual/filter.html
-->
<FindBugsFilter>
<!--
Intentional fallthrough in switch statement
-->
<Match>
<Class name="org.apache.harmony.unpack200.BcBands" />
<Method name="read" />
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
</Match>
<!--
Our cache is testing String with ==
-->
<Match>
<Class name="org.apache.harmony.unpack200.SegmentConstantPoolArrayCache" />
<Method name="indexesForArrayKey" />
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
</Match>
</FindBugsFilter>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project name="Harmony Pack200 Test" default="test" basedir=".">
<property name="hy.hdk" location="../../.." />
<property name="test.jre.home" location="${hy.hdk}/jdk/jre" />
<property file="../test.properties" />
<property name="work.dir" value=".." />
<property name="target.dir" value=".." />
<property name="tests.output" location="../report" />
<property name="junit.jar" location="../junit.jar" />
<import file="../../ant/properties.xml" />
<target name="test" depends="test-module" />
<target name="test-module" depends="test-jre-vm-info">
<convert-test-as-class from="test.case" to="converted.tc.class" />
<run-hdk-tests module="pack200" jar="pack200_tests.jar">
<junit-elements>
<classpath location="resources" />
</junit-elements>
</run-hdk-tests>
</target>
</project>

@ -0,0 +1,328 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.GZIPOutputStream;
/**
* Archive is the main entry point to pack200 and represents a packed archive.
* An archive is constructed with either a JarInputStream and an output stream
* or a JarFile as input and an OutputStream. Options can be set, then
* <code>pack()</code> is called, to pack the Jar file into a pack200 archive.
*/
public class Archive {
private final JarInputStream jarInputStream;
private final OutputStream outputStream;
private JarFile jarFile;
private long currentSegmentSize;
private final PackingOptions options;
/**
* Creates an Archive with streams for the input and output.
*
* @param inputStream
* @param outputStream
* @param options - packing options (if null then defaults are used)
* @throws IOException
*/
public Archive(JarInputStream inputStream, OutputStream outputStream,
PackingOptions options) throws IOException {
jarInputStream = inputStream;
if(options == null) {
// use all defaults
options = new PackingOptions();
}
this.options = options;
if (options.isGzip()) {
outputStream = new GZIPOutputStream(outputStream);
}
this.outputStream = new BufferedOutputStream(outputStream);
PackingUtils.config(options);
}
/**
* Creates an Archive with the given input file and a stream for the output
*
* @param jarFile - the input file
* @param outputStream
* @param options - packing options (if null then defaults are used)
* @throws IOException
*/
public Archive(JarFile jarFile, OutputStream outputStream,
PackingOptions options) throws IOException {
if(options == null) { // use all defaults
options = new PackingOptions();
}
this.options = options;
if (options.isGzip()) {
outputStream = new GZIPOutputStream(outputStream);
}
this.outputStream = new BufferedOutputStream(outputStream);
this.jarFile = jarFile;
jarInputStream = null;
PackingUtils.config(options);
}
/**
* Pack the archive
* @throws Pack200Exception
* @throws IOException
*/
public void pack() throws Pack200Exception, IOException {
if (0 == options.getEffort()) {
doZeroEffortPack();
} else {
doNormalPack();
}
}
private void doZeroEffortPack() throws IOException, Pack200Exception {
PackingUtils.log("Start to perform a zero-effort packing");
if (jarInputStream != null) {
PackingUtils.copyThroughJar(jarInputStream, outputStream);
} else {
PackingUtils.copyThroughJar(jarFile, outputStream);
}
}
private void doNormalPack() throws IOException, Pack200Exception {
PackingUtils.log("Start to perform a normal packing");
List packingFileList;
if (jarInputStream != null) {
packingFileList = PackingUtils.getPackingFileListFromJar(
jarInputStream, options.isKeepFileOrder());
} else {
packingFileList = PackingUtils.getPackingFileListFromJar(jarFile,
options.isKeepFileOrder());
}
List segmentUnitList = splitIntoSegments(packingFileList);
int previousByteAmount = 0;
int packedByteAmount = 0;
int segmentSize = segmentUnitList.size();
SegmentUnit segmentUnit;
for (int index = 0; index < segmentSize; index++) {
segmentUnit = (SegmentUnit) segmentUnitList.get(index);
new Segment().pack(segmentUnit, outputStream, options);
previousByteAmount += segmentUnit.getByteAmount();
packedByteAmount += segmentUnit.getPackedByteAmount();
}
PackingUtils.log("Total: Packed " + previousByteAmount
+ " input bytes of " + packingFileList.size() + " files into "
+ packedByteAmount + " bytes in " + segmentSize + " segments");
outputStream.close();
}
private List splitIntoSegments(List packingFileList) throws IOException,
Pack200Exception {
List segmentUnitList = new ArrayList();
List classes = new ArrayList();
List files = new ArrayList();
long segmentLimit = options.getSegmentLimit();
int size = packingFileList.size();
PackingFile packingFile;
for (int index = 0; index < size; index++) {
packingFile = (PackingFile) packingFileList.get(index);
if (!addJarEntry(packingFile, classes, files)) {
// not added because segment has reached maximum size
segmentUnitList.add(new SegmentUnit(classes, files));
classes = new ArrayList();
files = new ArrayList();
currentSegmentSize = 0;
// add the jar to a new segment
addJarEntry(packingFile, classes, files);
// ignore the size of first entry for compatibility with RI
currentSegmentSize = 0;
} else if (segmentLimit == 0 && estimateSize(packingFile) > 0) {
// create a new segment for each class unless size is 0
segmentUnitList.add(new SegmentUnit(classes, files));
classes = new ArrayList();
files = new ArrayList();
}
}
if (classes.size() > 0 && files.size() > 0) {
segmentUnitList.add(new SegmentUnit(classes, files));
}
return segmentUnitList;
}
private boolean addJarEntry(PackingFile packingFile, List javaClasses,
List files) throws IOException, Pack200Exception {
long segmentLimit = options.getSegmentLimit();
if (segmentLimit != -1 && segmentLimit != 0) {
// -1 is a special case where only one segment is created and
// 0 is a special case where one segment is created for each file
// except for files in "META-INF"
long packedSize = estimateSize(packingFile);
if (packedSize + currentSegmentSize > segmentLimit
&& currentSegmentSize > 0) {
// don't add this JarEntry to the current segment
return false;
} else {
// do add this JarEntry
currentSegmentSize += packedSize;
}
}
String name = packingFile.getName();
if (name.endsWith(".class") && !options.isPassFile(name)) {
Pack200ClassReader classParser = new Pack200ClassReader(
packingFile.contents);
classParser.setFileName(name);
javaClasses.add(classParser);
packingFile.contents = new byte[0];
}
files.add(packingFile);
return true;
}
private long estimateSize(PackingFile packingFile) {
// The heuristic used here is for compatibility with the RI and should
// not be changed
String name = packingFile.getName();
if (name.startsWith("META-INF") || name.startsWith("/META-INF")) {
return 0;
} else {
long fileSize = packingFile.contents.length;
if (fileSize < 0) {
fileSize = 0;
}
return name.length() + fileSize + 5;
}
}
static class SegmentUnit {
private List classList;
private List fileList;
private int byteAmount = 0;
private int packedByteAmount = 0;
public SegmentUnit(List classes, List files) {
classList = classes;
fileList = files;
// Calculate the amount of bytes in classes and files before packing
Pack200ClassReader classReader;
for (Iterator iterator = classList.iterator(); iterator.hasNext();) {
classReader = (Pack200ClassReader) iterator.next();
byteAmount += classReader.b.length;
}
PackingFile file;
for (Iterator iterator = fileList.iterator(); iterator.hasNext();) {
file = (PackingFile) iterator.next();
byteAmount += file.contents.length;
}
}
public List getClassList() {
return classList;
}
public int classListSize() {
return classList.size();
}
public int fileListSize() {
return fileList.size();
}
public List getFileList() {
return fileList;
}
public int getByteAmount() {
return byteAmount;
}
public int getPackedByteAmount() {
return packedByteAmount;
}
public void addPackedByteAmount(int amount) {
packedByteAmount += amount;
}
}
static class PackingFile {
private final String name;
private byte[] contents;
private final long modtime;
private final boolean deflateHint;
private final boolean isDirectory;
public PackingFile(String name, byte[] contents, long modtime) {
this.name = name;
this.contents = contents;
this.modtime = modtime;
deflateHint = false;
isDirectory = false;
}
public PackingFile(byte[] bytes, JarEntry jarEntry) {
name = jarEntry.getName();
contents = bytes;
modtime = jarEntry.getTime();
deflateHint = jarEntry.getMethod() == JarEntry.DEFLATED;
isDirectory = jarEntry.isDirectory();
}
public byte[] getContents() {
return contents;
}
public String getName() {
return name;
}
public long getModtime() {
return modtime;
}
public void setContents(byte[] contents) {
this.contents = contents;
}
public boolean isDefalteHint() {
return deflateHint;
}
public boolean isDirectory(){
return isDirectory;
}
}
}

@ -0,0 +1,258 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Attribute;
/**
* Attribute Definition bands define how any unknown attributes should be read
* by the decompressor.
*/
public class AttributeDefinitionBands extends BandSet {
public static final int CONTEXT_CLASS = 0;
public static final int CONTEXT_CODE = 3;
public static final int CONTEXT_FIELD = 1;
public static final int CONTEXT_METHOD = 2;
private final List classAttributeLayouts = new ArrayList();
private final List methodAttributeLayouts = new ArrayList();
private final List fieldAttributeLayouts = new ArrayList();
private final List codeAttributeLayouts = new ArrayList();
private final List attributeDefinitions = new ArrayList();
private final CpBands cpBands;
private final Segment segment;
public AttributeDefinitionBands(Segment segment, int effort,
Attribute[] attributePrototypes) {
super(effort, segment.getSegmentHeader());
this.cpBands = segment.getCpBands();
this.segment = segment;
Map classLayouts = new HashMap();
Map methodLayouts = new HashMap();
Map fieldLayouts = new HashMap();
Map codeLayouts = new HashMap();
for (int i = 0; i < attributePrototypes.length; i++) {
NewAttribute newAttribute = (NewAttribute) attributePrototypes[i];
if (newAttribute.isContextClass()) {
classLayouts.put(newAttribute.type, newAttribute.getLayout());
}
if (newAttribute.isContextMethod()) {
methodLayouts.put(newAttribute.type, newAttribute.getLayout());
}
if (newAttribute.isContextField()) {
fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
}
if (newAttribute.isContextCode()) {
codeLayouts.put(newAttribute.type, newAttribute.getLayout());
}
}
if (classLayouts.keySet().size() > 7) {
segmentHeader.setHave_class_flags_hi(true);
}
if (methodLayouts.keySet().size() > 6) {
segmentHeader.setHave_method_flags_hi(true);
}
if (fieldLayouts.keySet().size() > 10) {
segmentHeader.setHave_field_flags_hi(true);
}
if (codeLayouts.keySet().size() > 15) {
segmentHeader.setHave_code_flags_hi(true);
}
int[] availableClassIndices = new int[] { 25, 26, 27, 28, 29, 30, 31 };
if (classLayouts.size() > 7) {
availableClassIndices = addHighIndices(availableClassIndices);
}
addAttributeDefinitions(classLayouts, availableClassIndices,
CONTEXT_CLASS);
int[] availableMethodIndices = new int[] { 26, 27, 28, 29, 30, 31 };
if (methodAttributeLayouts.size() > 6) {
availableMethodIndices = addHighIndices(availableMethodIndices);
}
addAttributeDefinitions(methodLayouts, availableMethodIndices,
CONTEXT_METHOD);
int[] availableFieldIndices = new int[] { 18, 23, 24, 25, 26, 27, 28,
29, 30, 31 };
if (fieldAttributeLayouts.size() > 10) {
availableFieldIndices = addHighIndices(availableFieldIndices);
}
addAttributeDefinitions(fieldLayouts, availableFieldIndices,
CONTEXT_FIELD);
int[] availableCodeIndices = new int[] { 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31 };
if (codeAttributeLayouts.size() > 15) {
availableCodeIndices = addHighIndices(availableCodeIndices);
}
addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
}
/**
* All input classes for the segment have now been read in, so this method
* is called so that this class can calculate/complete anything it could not
* do while classes were being read.
*/
public void finaliseBands() {
addSyntheticDefinitions();
segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing attribute definition bands...");
int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
int[] attributeDefinitionName = new int[attributeDefinitions.size()];
int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
for (int i = 0; i < attributeDefinitionLayout.length; i++) {
AttributeDefinition def = (AttributeDefinition) attributeDefinitions
.get(i);
attributeDefinitionHeader[i] = def.contextType
| (def.index + 1 << 2);
attributeDefinitionName[i] = def.name.getIndex();
attributeDefinitionLayout[i] = def.layout.getIndex();
}
byte[] encodedBand = encodeBandInt("attributeDefinitionHeader",
attributeDefinitionHeader, Codec.BYTE1);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from attributeDefinitionHeader["
+ attributeDefinitionHeader.length + "]");
encodedBand = encodeBandInt("attributeDefinitionName",
attributeDefinitionName, Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from attributeDefinitionName["
+ attributeDefinitionName.length + "]");
encodedBand = encodeBandInt("attributeDefinitionLayout",
attributeDefinitionLayout, Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from attributeDefinitionLayout["
+ attributeDefinitionLayout.length + "]");
}
private void addSyntheticDefinitions() {
boolean anySytheticClasses = segment.getClassBands()
.isAnySyntheticClasses();
boolean anySyntheticMethods = segment.getClassBands()
.isAnySyntheticMethods();
boolean anySyntheticFields = segment.getClassBands()
.isAnySyntheticFields();
if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
CPUTF8 emptyUTF = cpBands.getCPUtf8("");
if (anySytheticClasses) {
attributeDefinitions.add(new AttributeDefinition(12,
CONTEXT_CLASS, syntheticUTF, emptyUTF));
}
if (anySyntheticMethods) {
attributeDefinitions.add(new AttributeDefinition(12,
CONTEXT_METHOD, syntheticUTF, emptyUTF));
}
if (anySyntheticFields) {
attributeDefinitions.add(new AttributeDefinition(12,
CONTEXT_FIELD, syntheticUTF, emptyUTF));
}
}
}
private int[] addHighIndices(int[] availableIndices) {
int[] temp = new int[availableIndices.length + 32];
for (int i = 0; i < availableIndices.length; i++) {
temp[i] = availableIndices[i];
}
int j = 32;
for (int i = availableIndices.length; i < temp.length; i++) {
temp[i] = j;
j++;
}
return temp;
}
private void addAttributeDefinitions(Map layouts, int[] availableIndices,
int contextType) {
int i = 0;
for (Iterator iterator = layouts.keySet().iterator(); iterator
.hasNext();) {
String name = (String) iterator.next();
String layout = (String) layouts.get(name);
int index = availableIndices[i];
AttributeDefinition definition = new AttributeDefinition(index,
contextType, cpBands.getCPUtf8(name), cpBands
.getCPUtf8(layout));
attributeDefinitions.add(definition);
switch (contextType) {
case CONTEXT_CLASS:
classAttributeLayouts.add(definition);
break;
case CONTEXT_METHOD:
methodAttributeLayouts.add(definition);
break;
case CONTEXT_FIELD:
fieldAttributeLayouts.add(definition);
break;
case CONTEXT_CODE:
codeAttributeLayouts.add(definition);
}
}
}
public List getClassAttributeLayouts() {
return classAttributeLayouts;
}
public List getMethodAttributeLayouts() {
return methodAttributeLayouts;
}
public List getFieldAttributeLayouts() {
return fieldAttributeLayouts;
}
public List getCodeAttributeLayouts() {
return codeAttributeLayouts;
}
public static class AttributeDefinition {
public int index;
public int contextType;
public CPUTF8 name;
public CPUTF8 layout;
public AttributeDefinition(int index, int contextType, CPUTF8 name,
CPUTF8 layout) {
this.index = index;
this.contextType = contextType;
this.name = name;
this.layout = layout;
}
}
}

@ -0,0 +1,516 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* A BHSD codec is a means of encoding integer values as a sequence of bytes or
* vice versa using a specified "BHSD" encoding mechanism. It uses a
* variable-length encoding and a modified sign representation such that small
* numbers are represented as a single byte, whilst larger numbers take more
* bytes to encode. The number may be signed or unsigned; if it is unsigned, it
* can be weighted towards positive numbers or equally distributed using a one's
* complement. The Codec also supports delta coding, where a sequence of numbers
* is represented as a series of first-order differences. So a delta encoding of
* the integers [1..10] would be represented as a sequence of 10x1s. This allows
* the absolute value of a coded integer to fall outside of the 'small number'
* range, whilst still being encoded as a single byte.
*
* A BHSD codec is configured with four parameters:
* <dl>
* <dt>B</dt>
* <dd>The maximum number of bytes that each value is encoded as. B must be a
* value between [1..5]. For a pass-through coding (where each byte is encoded
* as itself, aka {@link #BYTE1}, B is 1 (each byte takes a maximum of 1 byte).</dd>
* <dt>H</dt>
* <dd>The radix of the integer. Values are defined as a sequence of values,
* where value <code>n</code> is multiplied by <code>H^<sup>n</sup></code>.
* So the number 1234 may be represented as the sequence 4 3 2 1 with a radix
* (H) of 10. Note that other permutations are also possible; 43 2 1 will also
* encode 1234. The co-parameter L is defined as 256-H. This is important
* because only the last value in a sequence may be &lt; L; all prior values
* must be &gt; L.</dd>
* <dt>S</dt>
* <dd>Whether the codec represents signed values (or not). This may have 3
* values; 0 (unsigned), 1 (signed, one's complement) or 2 (signed, two's
* complement)</dd>
* <dt>D</dt>
* <dd>Whether the codec represents a delta encoding. This may be 0 (no delta)
* or 1 (delta encoding). A delta encoding of 1 indicates that values are
* cumulative; a sequence of <code>1 1 1 1 1</code> will represent the
* sequence <code>1 2 3 4 5</code>. For this reason, the codec supports two
* variants of decode; one {@link #decode(InputStream, long) with} and one
* {@link #decode(InputStream) without} a <code>last</code> parameter. If the
* codec is a non-delta encoding, then the value is ignored if passed. If the
* codec is a delta encoding, it is a run-time error to call the value without
* the extra parameter, and the previous value should be returned. (It was
* designed this way to support multi-threaded access without requiring a new
* instance of the Codec to be cloned for each use.)
* <dt>
* </dl>
*
* Codecs are notated as (B,H,S,D) and either D or S,D may be omitted if zero.
* Thus {@link #BYTE1} is denoted (1,256,0,0) or (1,256). The
* {@link #toString()} method prints out the condensed form of the encoding.
* Often, the last character in the name ({@link #BYTE1}, {@link #UNSIGNED5})
* gives a clue as to the B value. Those that start with U ({@link #UDELTA5},
* {@link #UNSIGNED5}) are unsigned; otherwise, in most cases, they are signed.
* The presence of the word Delta ({@link #DELTA5}, {@link #UDELTA5})
* indicates a delta encoding is used.
*
*/
public final class BHSDCodec extends Codec {
/**
* The maximum number of bytes in each coding word
*/
private final int b;
/**
* Whether delta encoding is used (0=false,1=true)
*/
private final int d;
/**
* The radix of the encoding
*/
private final int h;
/**
* The co-parameter of h; 256-h
*/
private final int l;
/**
* Represents signed numbers or not (0=unsigned,1/2=signed)
*/
private final int s;
private long cardinality;
private final long smallest;
private final long largest;
/**
* radix^i powers
*/
private final long[] powers;
/**
* Constructs an unsigned, non-delta Codec with the given B and H values.
*
* @param b
* the maximum number of bytes that a value can be encoded as
* [1..5]
* @param h
* the radix of the encoding [1..256]
*/
public BHSDCodec(int b, int h) {
this(b, h, 0, 0);
}
/**
* Constructs a non-delta Codec with the given B, H and S values.
*
* @param b
* the maximum number of bytes that a value can be encoded as
* [1..5]
* @param h
* the radix of the encoding [1..256]
* @param s
* whether the encoding represents signed numbers (s=0 is
* unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
*/
public BHSDCodec(int b, int h, int s) {
this(b, h, s, 0);
}
/**
* Constructs a Codec with the given B, H, S and D values.
*
* @param b
* the maximum number of bytes that a value can be encoded as
* [1..5]
* @param h
* the radix of the encoding [1..256]
* @param s
* whether the encoding represents signed numbers (s=0 is
* unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
* @param d
* whether this is a delta encoding (d=0 is non-delta; d=1 is
* delta)
*/
public BHSDCodec(int b, int h, int s, int d) {
if (b < 1 || b > 5) {
throw new IllegalArgumentException("1<=b<=5");
}
if (h < 1 || h > 256) {
throw new IllegalArgumentException("1<=h<=256");
}
if (s < 0 || s > 2) {
throw new IllegalArgumentException("0<=s<=2");
}
if (d < 0 || d > 1) {
throw new IllegalArgumentException("0<=d<=1");
}
if (b == 1 && h != 256) {
throw new IllegalArgumentException("b=1 -> h=256");
}
if (h == 256 && b == 5) {
throw new IllegalArgumentException("h=256 -> b!=5");
}
this.b = b;
this.h = h;
this.s = s;
this.d = d;
this.l = 256 - h;
if (h == 1) {
cardinality = b * 255 + 1;
} else {
cardinality = (long) ((long) (l * (1 - Math.pow(h, b)) / (1 - h)) + Math
.pow(h, b));
}
smallest = calculateSmallest();
largest = calculateLargest();
powers = new long[b];
for(int c = 0; c < b; c++) {
powers[c] = (long)Math.pow(h, c);
}
}
/**
* Returns the cardinality of this codec; that is, the number of distinct
* values that it can contain.
*
* @return the cardinality of this codec
*/
public long cardinality() {
return cardinality;
}
public int decode(InputStream in) throws IOException, Pack200Exception {
if (d != 0) {
throw new Pack200Exception(
"Delta encoding used without passing in last value; this is a coding error");
}
return decode(in, 0);
}
public int decode(InputStream in, long last) throws IOException,
Pack200Exception {
int n = 0;
long z = 0;
long x = 0;
do {
x = in.read();
lastBandLength ++;
z += x * powers[n];
n++;
} while (x >= l && n < b);
if (x == -1) {
throw new EOFException("End of stream reached whilst decoding");
}
if (isSigned()) {
int u = ((1 << s) - 1);
if ((z & u) == u) {
z = z >>> s ^ -1L;
} else {
z = z - (z >>> s);
}
}
// This algorithm does the same thing, but is probably slower. Leaving
// in for now for readability
// if(isSigned()) {
// long u = z;
// long twoPowS = (long)Math.pow(2, s);
// double twoPowSMinusOne = twoPowS-1;
// if(u % twoPowS < twoPowSMinusOne) {
// if(cardinality < Math.pow(2, 32)) {
// z = (long) (u - (Math.floor(u/ twoPowS)));
// } else {
// z = cast32((long) (u - (Math.floor(u/ twoPowS))));
// }
// } else {
// z = (long) (-Math.floor(u/ twoPowS) - 1);
// }
// }
if (isDelta()) {
z += last;
}
return (int)z;
}
public int[] decodeInts(int n, InputStream in) throws IOException,
Pack200Exception {
int[] band = super.decodeInts(n, in);
if (isDelta()) {
for (int i = 0; i < band.length; i++) {
while (band[i] > largest) {
band[i] -= cardinality;
}
while (band[i] < smallest) {
band[i] += cardinality;
}
}
}
return band;
}
public int[] decodeInts(int n, InputStream in, int firstValue)
throws IOException, Pack200Exception {
int[] band = super.decodeInts(n, in, firstValue);
if (isDelta()) {
for (int i = 0; i < band.length; i++) {
while (band[i] > largest) {
band[i] -= cardinality;
}
while (band[i] < smallest) {
band[i] += cardinality;
}
}
}
return band;
}
// private long cast32(long u) {
// u = (long) ((long) ((u + Math.pow(2, 31)) % Math.pow(2, 32)) -
// Math.pow(2, 31));
// return u;
// }
/**
* True if this encoding can code the given value
*
* @param value
* the value to check
* @return <code>true</code> if the encoding can encode this value
*/
public boolean encodes(long value) {
return value >= smallest && value <= largest;
}
public byte[] encode(int value, int last) throws Pack200Exception {
if(!encodes(value)) {
throw new Pack200Exception("The codec " + toString()
+ " does not encode the value " + value);
}
long z = value;
if (isDelta()) {
z -= last;
}
if (isSigned()) {
if(z < Integer.MIN_VALUE) {
z += 4294967296L;
} else if (z > Integer.MAX_VALUE) {
z -= 4294967296L;
}
if (z < 0) {
z = (-z << s) - 1;
} else {
if (s == 1) {
z = z << s;
} else {
z += (z - z % 3) / 3;
}
}
} else {
if (z < 0) {
// Need to use integer overflow here to represent negatives.
if (cardinality < 4294967296L) {
z += cardinality;
} else {
z += 4294967296L; // this value is equal to (1 << 32).
}
}
}
if (z < 0) {
throw new Pack200Exception("unable to encode");
}
List byteList = new ArrayList();
for (int n = 0; n < b; n++) {
long byteN;
if (z < l) {
byteN = z;
} else {
byteN = z % h;
while (byteN < l) {
byteN += h;
}
}
byteList.add(new Byte((byte) byteN));
if (byteN < l) {
break;
}
z -= byteN;
z /= h;
}
byte[] bytes = new byte[byteList.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = ((Byte) byteList.get(i)).byteValue();
}
return bytes;
}
public byte[] encode(int value) throws Pack200Exception {
return encode(value, 0);
}
/**
* Returns true if this codec is a delta codec
*
* @return true if this codec is a delta codec
*/
public boolean isDelta() {
return d != 0;
}
/**
* Returns true if this codec is a signed codec
*
* @return true if this codec is a signed codec
*/
public boolean isSigned() {
return s != 0;
}
/**
* Returns the largest value that this codec can represent.
*
* @return the largest value that this codec can represent.
*/
public long largest() {
return largest;
}
private long calculateLargest() {
long result;
// TODO This can probably be optimized into a better mathematical
// statement
if (d == 1) {
BHSDCodec bh0 = new BHSDCodec(b, h);
return bh0.largest();
} else if (s == 0) {
result = cardinality() - 1;
} else if (s == 1) {
result = cardinality() / 2 - 1;
} else if (s == 2) {
result = (3L * cardinality()) / 4 - 1;
} else {
throw new Error("Unknown s value");
}
return Math.min((s == 0 ? ((long) Integer.MAX_VALUE) << 1
: Integer.MAX_VALUE) - 1, result);
}
/**
* Returns the smallest value that this codec can represent.
*
* @return the smallest value that this codec can represent.
*/
public long smallest() {
return smallest;
}
private long calculateSmallest() {
long result;
if (d == 1 || !isSigned()) {
if (cardinality >= 4294967296L) { // 2^32
result = Integer.MIN_VALUE;
} else {
result = 0;
}
} else {
result = Math.max(Integer.MIN_VALUE, -cardinality() / (1 << s));
}
return result;
}
/**
* Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing
* zero fields are not shown.
*/
public String toString() {
StringBuffer buffer = new StringBuffer(11);
buffer.append('(');
buffer.append(b);
buffer.append(',');
buffer.append(h);
if (s != 0 || d != 0) {
buffer.append(',');
buffer.append(s);
}
if (d != 0) {
buffer.append(',');
buffer.append(d);
}
buffer.append(')');
return buffer.toString();
}
/**
* @return the b
*/
public int getB() {
return b;
}
/**
* @return the h
*/
public int getH() {
return h;
}
/**
* @return the s
*/
public int getS() {
return s;
}
/**
* @return the l
*/
public int getL() {
return l;
}
public boolean equals(Object o) {
if (o instanceof BHSDCodec) {
BHSDCodec codec = (BHSDCodec) o;
return codec.b == b && codec.h == h && codec.s == s && codec.d == d;
}
return false;
}
public int hashCode() {
return ((b* 37 + h) * 37 + s) * 37 + d;
}
}

@ -0,0 +1,808 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Abstract superclass for a set of bands
*/
public abstract class BandSet {
protected final SegmentHeader segmentHeader;
final int effort;
// Minimum size of band for each effort level where we consider alternative codecs
// Note: these values have been tuned - please test carefully if changing them
private static final int[] effortThresholds = new int[] {0, 0, 1000, 500, 100, 100, 100, 100, 100, 0};
private long[] canonicalLargest;
private long[] canonicalSmallest;
/**
* Create a new BandSet
* @param effort - the packing effort to be used (must be 1-9)
* @param header - the segment header
*/
public BandSet(int effort, SegmentHeader header) {
this.effort = effort;
this.segmentHeader = header;
}
/**
* Write the packed set of bands to the given output stream
* @param out
* @throws IOException
* @throws Pack200Exception
*/
public abstract void pack(OutputStream out) throws IOException, Pack200Exception;
/**
* Encode a band without considering other Codecs
* @param band - the band
* @param codec - the Codec to use
* @return the encoded band
* @throws Pack200Exception
*/
public byte[] encodeScalar(int[] band, BHSDCodec codec) throws Pack200Exception {
return codec.encode(band);
}
/**
* Encode a single value with the given Codec
* @param value - the value to encode
* @param codec - Codec to use
* @return the encoded value
* @throws Pack200Exception
*/
public byte[] encodeScalar(int value, BHSDCodec codec) throws Pack200Exception {
return codec.encode(value);
}
/**
* Encode a band of integers. The default codec may be used, but other
* Codecs are considered if effort is greater than 1.
*
* @param name
* - name of the band (used for debugging)
* @param ints
* - the band
* @param defaultCodec
* - the default Codec
* @return the encoded band
* @throws Pack200Exception
*/
public byte[] encodeBandInt(String name, int[] ints, BHSDCodec defaultCodec) throws Pack200Exception {
byte[] encodedBand = null;
// Useful for debugging
// if(ints.length > 0) {
// System.out.println("encoding " + name + " " + ints.length);
// }
if(effort > 1 && (ints.length >= effortThresholds[effort])) {
BandAnalysisResults results = analyseBand(name, ints, defaultCodec);
Codec betterCodec = results.betterCodec;
encodedBand = results.encodedBand;
if(betterCodec != null) {
if(betterCodec instanceof BHSDCodec) {
int[] specifierBand = CodecEncoding.getSpecifier(betterCodec, defaultCodec);
int specifier = specifierBand[0];
if(specifierBand.length > 1) {
for (int i = 1; i < specifierBand.length; i++) {
segmentHeader.appendBandCodingSpecifier(specifierBand[i]);
}
}
if(defaultCodec.isSigned()) {
specifier = -1 -specifier;
} else {
specifier = specifier + defaultCodec.getL();
}
byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
byte[] band = new byte[specifierEncoded.length + encodedBand.length];
System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
return band;
} else if (betterCodec instanceof PopulationCodec) {
int[] extraSpecifierInfo = results.extraMetadata;
for (int i = 0; i < extraSpecifierInfo.length; i++) {
segmentHeader.appendBandCodingSpecifier(extraSpecifierInfo[i]);
}
return encodedBand;
} else if (betterCodec instanceof RunCodec) {
}
}
}
// If we get here then we've decided to use the default codec.
if(ints.length > 0) {
if(encodedBand == null) {
encodedBand = defaultCodec.encode(ints);
}
int first = ints[0];
if(defaultCodec.getB() != 1) {
if (defaultCodec.isSigned() && first >= -256 && first <= -1) {
int specifier = -1 - CodecEncoding.getSpecifierForDefaultCodec(defaultCodec);
byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
byte[] band = new byte[specifierEncoded.length + encodedBand.length];
System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
return band;
} else if (!defaultCodec.isSigned() && first >= defaultCodec.getL()
&& first <= defaultCodec.getL() + 255) {
int specifier = CodecEncoding.getSpecifierForDefaultCodec(defaultCodec) + defaultCodec.getL();
byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
byte[] band = new byte[specifierEncoded.length + encodedBand.length];
System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
return band;
}
}
return encodedBand;
}
return new byte[0];
}
private BandAnalysisResults analyseBand(String name, int[] band,
BHSDCodec defaultCodec) throws Pack200Exception {
BandAnalysisResults results = new BandAnalysisResults();
if(canonicalLargest == null) {
canonicalLargest = new long[116];
canonicalSmallest = new long[116];
for (int i = 1; i < canonicalLargest.length; i++) {
canonicalLargest[i] = CodecEncoding.getCanonicalCodec(i).largest();
canonicalSmallest[i] = CodecEncoding.getCanonicalCodec(i).smallest();
}
}
BandData bandData = new BandData(band);
// Check that there is a reasonable saving to be made
byte[] encoded = defaultCodec.encode(band);
results.encodedBand = encoded;
// Note: these values have been tuned - please test carefully if changing them
if(encoded.length <= band.length + 23 - 2*effort) { // TODO: tweak
return results;
}
// Check if we can use BYTE1 as that's a 1:1 mapping if we can
if(!bandData.anyNegatives() && bandData.largest <= Codec.BYTE1.largest()) {
results.encodedBand = Codec.BYTE1.encode(band) ;
results.betterCodec = Codec.BYTE1;
return results;
}
// Consider a population codec (but can't be nested)
if(effort > 3 && !name.equals("POPULATION")) {
int numDistinctValues = bandData.numDistinctValues();
float distinctValuesAsProportion = (float)numDistinctValues / (float)band.length;
// Note: these values have been tuned - please test carefully if changing them
if(numDistinctValues < 100 || distinctValuesAsProportion < 0.02 || (effort > 6 && distinctValuesAsProportion < 0.04)) { // TODO: tweak
encodeWithPopulationCodec(name, band, defaultCodec, bandData, results);
if(timeToStop(results)) {
return results;
}
}
}
List codecFamiliesToTry = new ArrayList();
// See if the deltas are mainly small increments
if(bandData.mainlyPositiveDeltas() && bandData.mainlySmallDeltas()) {
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs2);
}
if (bandData.wellCorrelated()) { // Try delta encodings
if (bandData.mainlyPositiveDeltas()) {
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
} else {
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
}
} else {
if (bandData.anyNegatives()) {
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
} else {
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
}
}
if(name.equalsIgnoreCase("cpint")) {
System.out.print("");
}
for (Iterator iterator = codecFamiliesToTry.iterator(); iterator
.hasNext();) {
BHSDCodec[] family = (BHSDCodec[]) iterator.next();
tryCodecs(name, band, defaultCodec, bandData, results, encoded,
family);
if (timeToStop(results)) {
break;
}
}
return results;
}
private boolean timeToStop(BandAnalysisResults results) {
// if tried more than effort number of codecs for this band then return
// Note: these values have been tuned - please test carefully if changing them
if(effort > 6) {
return results.numCodecsTried >= effort * 2;
}
return results.numCodecsTried >= effort;
// May want to also check how much we've saved if performance needs improving, e.g. saved more than effort*2 %
// || (float)results.saved/(float)results.encodedBand.length > (float)effort * 2/100;
}
private void tryCodecs(String name, int[] band, BHSDCodec defaultCodec, BandData bandData,
BandAnalysisResults results, byte[] encoded,
BHSDCodec[] potentialCodecs) throws Pack200Exception {
for (int i = 0; i < potentialCodecs.length; i++) {
BHSDCodec potential = potentialCodecs[i];
if(potential.equals(defaultCodec)) {
return; // don't try codecs with greater cardinality in the same 'family' as the default codec as there won't be any savings
}
if (potential.isDelta()) {
if (potential.largest() >= bandData.largestDelta
&& potential.smallest() <= bandData.smallestDelta
&& potential.largest() >= bandData.largest
&& potential.smallest() <= bandData.smallest) {
// TODO: can represent some negative deltas with overflow
byte[] encoded2 = potential.encode(band);
results.numCodecsTried++;
byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
.getSpecifier(potential, null));
int saved = encoded.length - encoded2.length
- specifierEncoded.length;
if (saved > results.saved) {
results.betterCodec = potential;
results.encodedBand = encoded2;
results.saved = saved;
}
}
} else if (potential.largest() >= bandData.largest
&& potential.smallest() <= bandData.smallest) {
byte[] encoded2 = potential.encode(band);
results.numCodecsTried++;
byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
.getSpecifier(potential, null));
int saved = encoded.length - encoded2.length
- specifierEncoded.length;
if (saved > results.saved) {
results.betterCodec = potential;
results.encodedBand = encoded2;
results.saved = saved;
}
}
if(timeToStop(results)) {
return;
}
}
}
/**
* Returns true if the name of the source file can be predicted from the
* class name
*
* @param className
* the class name
* @param sourceFileName
* the source file name
*/
public boolean isPredictableSourceFileName(String className, String sourceFileName) {
if (className.indexOf('.') != -1) {
className = className.substring(className.lastIndexOf('.') + 1);
}
if (className.indexOf('$') != -1) {
className = className.substring(0, className.indexOf('$'));
}
className += ".java";
return className.equals(sourceFileName);
}
// This could be useful if further enhancements are done but is not currently used
//
// private void encodeWithRunCodec(String name, int[] band, int index,
// BHSDCodec defaultCodec, BandData bandData,
// BandAnalysisResults results) throws Pack200Exception {
// int[] firstBand = new int[index];
// int[] secondBand = new int[band.length - index];
// System.arraycopy(band, 0, firstBand, 0, index);
// System.arraycopy(band, index, secondBand, 0, secondBand.length);
// BandAnalysisResults firstResults = analyseBand(name + "A", firstBand, defaultCodec);
// BandAnalysisResults secondResults = analyseBand(name + "B", secondBand, defaultCodec);
// int specifier = 117;
// byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
// int totalLength = firstResults.encodedBand.length + secondResults.encodedBand.length + specifierEncoded.length + 4; // TODO actual
// if(totalLength < results.encodedBand.length) {
// System.out.println("using run codec");
// results.saved += results.encodedBand.length - totalLength;
// byte[] encodedBand = new byte[specifierEncoded.length + firstResults.encodedBand.length + secondResults.encodedBand.length];
// System.arraycopy(specifierEncoded, 0, encodedBand, 0, specifierEncoded.length);
// System.arraycopy(firstResults.encodedBand, 0, encodedBand, specifierEncoded.length, firstResults.encodedBand.length);
// System.arraycopy(secondResults.encodedBand, 0, encodedBand, specifierEncoded.length + firstResults.encodedBand.length, secondResults.encodedBand.length);
// results.encodedBand = encodedBand;
// results.betterCodec = new RunCodec(index, firstResults.betterCodec, secondResults.betterCodec);
// }
// }
private void encodeWithPopulationCodec(String name, int[] band,
BHSDCodec defaultCodec, BandData bandData, BandAnalysisResults results) throws Pack200Exception {
results.numCodecsTried += 3; // quite a bit more effort to try this codec
final Map distinctValues = bandData.distinctValues;
List favoured = new ArrayList();
for (Iterator iterator = distinctValues.keySet().iterator(); iterator
.hasNext();) {
Integer value = (Integer) iterator.next();
Integer count = (Integer) distinctValues.get(value);
if(count.intValue() > 2 || distinctValues.size() < 256) { // TODO: tweak
favoured.add(value);
}
}
// Sort the favoured list with the most commonly occurring first
if(distinctValues.size() > 255) {
Collections.sort(favoured, new Comparator() {
public int compare(Object arg0, Object arg1) {
return ((Integer)distinctValues.get(arg1)).compareTo((Integer)distinctValues.get(arg0));
}
});
}
IntList unfavoured = new IntList();
Map favouredToIndex = new HashMap();
for (int i = 0; i < favoured.size(); i++) {
Integer value = (Integer) favoured.get(i);
favouredToIndex.put(value, new Integer(i));
}
int[] tokens = new int[band.length];
for (int i = 0; i < band.length; i++) {
Integer favouredIndex = (Integer)favouredToIndex.get(new Integer(band[i]));
if(favouredIndex == null) {
tokens[i] = 0;
unfavoured.add(band[i]);
} else {
tokens[i] = favouredIndex.intValue() + 1;
}
}
favoured.add(favoured.get(favoured.size() - 1)); // repeat last value
int[] favouredBand = integerListToArray(favoured);
int[] unfavouredBand = unfavoured.toArray();
// Analyse the three bands to get the best codec
BandAnalysisResults favouredResults = analyseBand("POPULATION", favouredBand, defaultCodec);
BandAnalysisResults unfavouredResults = analyseBand("POPULATION", unfavouredBand, defaultCodec);
int tdefL = 0;
int l = 0;
Codec tokenCodec = null;
byte[] tokensEncoded;
int k = favoured.size() - 1;
if(k < 256) {
tdefL = 1;
tokensEncoded = Codec.BYTE1.encode(tokens);
} else {
BandAnalysisResults tokenResults = analyseBand("POPULATION", tokens, defaultCodec);
tokenCodec = tokenResults.betterCodec;
tokensEncoded = tokenResults.encodedBand;
if(tokenCodec == null) {
tokenCodec = defaultCodec;
}
l = ((BHSDCodec) tokenCodec).getL();
int h = ((BHSDCodec) tokenCodec).getH();
int s = ((BHSDCodec) tokenCodec).getS();
int b = ((BHSDCodec) tokenCodec).getB();
int d = ((BHSDCodec) tokenCodec).isDelta() ? 1 : 0;
if(s == 0 && d == 0) {
boolean canUseTDefL = true;
if(b > 1) {
BHSDCodec oneLowerB = new BHSDCodec(b-1, h);
if(oneLowerB.largest() >= k) {
canUseTDefL = false;
}
}
if(canUseTDefL) {
switch (l) {
case 4:
tdefL = 1;
break;
case 8:
tdefL = 2;
break;
case 16:
tdefL = 3;
break;
case 32:
tdefL = 4;
break;
case 64:
tdefL = 5;
break;
case 128:
tdefL = 6;
break;
case 192:
tdefL = 7;
break;
case 224:
tdefL = 8;
break;
case 240:
tdefL = 9;
break;
case 248:
tdefL = 10;
break;
case 252:
tdefL = 11;
break;
}
}
}
}
byte[] favouredEncoded = favouredResults.encodedBand;
byte[] unfavouredEncoded = unfavouredResults.encodedBand;
Codec favouredCodec = favouredResults.betterCodec;
Codec unfavouredCodec = unfavouredResults.betterCodec;
int specifier = 141 + (favouredCodec == null ? 1 : 0) + (4 * tdefL) + (unfavouredCodec == null ? 2 : 0);
IntList extraBandMetadata = new IntList(3);
if(favouredCodec != null) {
int[] specifiers = CodecEncoding.getSpecifier(favouredCodec, null);
for (int i = 0; i < specifiers.length; i++) {
extraBandMetadata.add(specifiers[i]);
}
}
if(tdefL == 0) {
int[] specifiers = CodecEncoding.getSpecifier(tokenCodec, null);
for (int i = 0; i < specifiers.length; i++) {
extraBandMetadata.add(specifiers[i]);
}
}
if(unfavouredCodec != null) {
int[] specifiers = CodecEncoding.getSpecifier(unfavouredCodec, null);
for (int i = 0; i < specifiers.length; i++) {
extraBandMetadata.add(specifiers[i]);
}
}
int[] extraMetadata = extraBandMetadata.toArray();
byte[] extraMetadataEncoded = Codec.UNSIGNED5.encode(extraMetadata);
if(defaultCodec.isSigned()) {
specifier = -1 -specifier;
} else {
specifier = specifier + defaultCodec.getL();
}
byte[] firstValueEncoded = defaultCodec.encode(new int[] {specifier});
int totalBandLength = firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length;
if(totalBandLength + extraMetadataEncoded.length < results.encodedBand.length) {
results.saved += results.encodedBand.length - (totalBandLength + extraMetadataEncoded.length);
byte[] encodedBand = new byte[totalBandLength];
System.arraycopy(firstValueEncoded, 0, encodedBand, 0, firstValueEncoded.length);
System.arraycopy(favouredEncoded, 0, encodedBand, firstValueEncoded.length, favouredEncoded.length);
System.arraycopy(tokensEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length, tokensEncoded.length);
System.arraycopy(unfavouredEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length, unfavouredEncoded.length);
results.encodedBand = encodedBand;
results.extraMetadata = extraMetadata;
if(l != 0) {
results.betterCodec = new PopulationCodec(favouredCodec, l, unfavouredCodec);
} else {
results.betterCodec = new PopulationCodec(favouredCodec, tokenCodec, unfavouredCodec);
}
}
}
/**
* Encode a band of longs (values are split into their high and low 32 bits
* and then encoded as two separate bands
*
* @param name
* - name of the band (for debugging purposes)
* @param flags
* - the band
* @param loCodec
* - Codec for the low 32-bits band
* @param hiCodec
* - Codec for the high 32-bits band
* @param haveHiFlags
* - ignores the high band if true as all values would be zero
* @return the encoded band
* @throws Pack200Exception
*/
protected byte[] encodeFlags(String name, long[] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
boolean haveHiFlags) throws Pack200Exception {
if(!haveHiFlags) {
int[] loBits = new int[flags.length];
for (int i = 0; i < flags.length; i++) {
loBits[i] = (int) flags[i];
}
return encodeBandInt(name, loBits, loCodec);
} else {
int[] hiBits = new int[flags.length];
int[] loBits = new int[flags.length];
for (int i = 0; i < flags.length; i++) {
long l = flags[i];
hiBits[i] = (int) (l >> 32);
loBits[i] = (int) l;
}
byte[] hi = encodeBandInt(name, hiBits, hiCodec);
byte[] lo = encodeBandInt(name, loBits, loCodec);
byte[] total = new byte[hi.length + lo.length];
System.arraycopy(hi, 0, total, 0, hi.length);
System.arraycopy(lo, 0, total, hi.length + 1, lo.length);
return total;
}
}
/**
* Converts a list of Integers to an int[] array
*/
protected int[] integerListToArray(List integerList) {
int[] array = new int[integerList.size()];
for (int i = 0; i < array.length; i++) {
array[i] = ((Integer)integerList.get(i)).intValue();
}
return array;
}
/**
* Converts a list of Longs to an long[] array
*/
protected long[] longListToArray(List longList) {
long[] array = new long[longList.size()];
for (int i = 0; i < array.length; i++) {
array[i] = ((Long)longList.get(i)).longValue();
}
return array;
}
/**
* Converts a list of ConstantPoolEntrys to an int[] array of their indices
*/
protected int[] cpEntryListToArray(List list) {
int[] array = new int[list.size()];
for (int i = 0; i < array.length; i++) {
array[i] = ((ConstantPoolEntry)list.get(i)).getIndex();
if(array[i] < 0) {
throw new RuntimeException("Index should be > 0");
}
}
return array;
}
/**
* Converts a list of ConstantPoolEntrys or nulls to an int[] array of their
* indices +1 (or 0 for nulls)
*/
protected int[] cpEntryOrNullListToArray(List theList) {
int[] array = new int[theList.size()];
for (int j = 0; j < array.length; j++) {
ConstantPoolEntry cpEntry = (ConstantPoolEntry) theList.get(j);
array[j] = cpEntry == null ? 0 : cpEntry.getIndex() + 1;
if(cpEntry != null && cpEntry.getIndex() < 0) {
throw new RuntimeException("Index should be > 0");
}
}
return array;
}
protected byte[] encodeFlags(String name, long[][] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
boolean haveHiFlags) throws Pack200Exception {
return encodeFlags(name, flatten(flags), loCodec, hiCodec, haveHiFlags);
}
/*
* Flatten a 2-dimension array into a 1-dimension array
*/
private long[] flatten(long[][] flags) {
int totalSize = 0;
for (int i = 0; i < flags.length; i++) {
totalSize += flags[i].length;
}
long[] flatArray = new long[totalSize];
int index = 0;
for (int i = 0; i < flags.length; i++) {
for (int j = 0; j < flags[i].length; j++) {
flatArray[index] = flags[i][j];
index++;
}
}
return flatArray;
}
/**
* BandData represents information about a band, e.g. largest value etc
* and is used in the heuristics that calculate whether an alternative
* Codec could make the encoded band smaller.
*/
public class BandData {
private final int[] band;
private int smallest = Integer.MAX_VALUE;
private int largest = Integer.MIN_VALUE;
private int smallestDelta;
private int largestDelta;
private int deltaIsAscending = 0;
private int smallDeltaCount = 0;
private double averageAbsoluteDelta = 0;
private double averageAbsoluteValue = 0;
private Map distinctValues;
/**
* Create a new instance of BandData. The band is then analysed.
* @param band - the band of integers
*/
public BandData(int[] band) {
this.band = band;
Integer one = new Integer(1);
for (int i = 0; i < band.length; i++) {
if(band[i] < smallest) {
smallest = band[i];
}
if(band[i] > largest) {
largest = band[i];
}
if(i != 0) {
int delta = band[i] - band[i - 1];
if(delta < smallestDelta) {
smallestDelta = delta;
}
if(delta > largestDelta) {
largestDelta = delta;
}
if(delta >= 0) {
deltaIsAscending++;
}
averageAbsoluteDelta += (double)Math.abs(delta)/(double)(band.length - 1);
if(Math.abs(delta) < 256) {
smallDeltaCount++;
}
} else {
smallestDelta = band[0];
largestDelta = band[0];
}
averageAbsoluteValue += (double)Math.abs(band[i])/(double)band.length;
if(effort > 3) { // do calculations needed to consider population codec
if(distinctValues == null) {
distinctValues = new HashMap();
}
Integer value = new Integer(band[i]);
Integer count = (Integer) distinctValues.get(value);
if(count == null) {
count = one;
} else {
count = new Integer(count.intValue() + 1);
}
distinctValues.put(value, count);
}
}
}
/**
* Returns true if the deltas between adjacent band elements are mainly
* small (heuristic)
*/
public boolean mainlySmallDeltas() {
// Note: the value below has been tuned - please test carefully if changing it
return (float)smallDeltaCount/(float)band.length > 0.7F;
}
/**
* Returns true if the band is well correlated (i.e. would be suitable
* for a delta encoding) (heuristic)
*/
public boolean wellCorrelated() {
// Note: the value below has been tuned - please test carefully if changing it
return averageAbsoluteDelta * 3.1 < averageAbsoluteValue;
}
/**
* Returns true if the band deltas are mainly positive (heuristic)
*/
public boolean mainlyPositiveDeltas() {
// Note: the value below has been tuned - please test carefully if changing it
return (float)deltaIsAscending/(float)band.length > 0.95F;
}
/**
* Returns true if any band elements are negative
*/
public boolean anyNegatives() {
return smallest < 0;
}
/**
* Returns the total number of distinct values found in the band
*/
public int numDistinctValues() {
if(distinctValues == null) {
return band.length;
}
return distinctValues.size();
}
}
/**
* Results obtained by trying different Codecs to encode a band
*/
public class BandAnalysisResults {
// The number of Codecs tried so far
private int numCodecsTried = 0;
// The number of bytes saved by using betterCodec instead of the default codec
private int saved = 0;
// Extra metadata to pass to the segment header (to be appended to the
// band_headers band)
private int[] extraMetadata;
// The results of encoding the band with betterCodec
private byte[] encodedBand;
// The best Codec found so far, or should be null if the default is the
// best so far
private Codec betterCodec;
}
}

@ -0,0 +1,582 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Label;
/**
* Bytecode bands (corresponds to the <code>bc_bands</code> set of bands in the
* pack200 specification)
*/
public class BcBands extends BandSet {
private final CpBands cpBands;
private final Segment segment;
public BcBands(CpBands cpBands, Segment segment, int effort) {
super(effort, segment.getSegmentHeader());
this.cpBands = cpBands;
this.segment = segment;
}
private final IntList bcCodes = new IntList();
private final IntList bcCaseCount = new IntList();
private final IntList bcCaseValue = new IntList();
private final IntList bcByte = new IntList();
private final IntList bcShort = new IntList();
private final IntList bcLocal = new IntList();
private final List bcLabel = new ArrayList();
private final List bcIntref = new ArrayList();
private final List bcFloatRef = new ArrayList();
private final List bcLongRef = new ArrayList();
private final List bcDoubleRef = new ArrayList();
private final List bcStringRef = new ArrayList();
private final List bcClassRef = new ArrayList();
private final List bcFieldRef = new ArrayList();
private final List bcMethodRef = new ArrayList();
private final List bcIMethodRef = new ArrayList();
private List bcThisField = new ArrayList();
private final List bcSuperField = new ArrayList();
private List bcThisMethod = new ArrayList();
private List bcSuperMethod = new ArrayList();
private List bcInitRef = new ArrayList();
private String currentClass;
private String superClass;
private String currentNewClass;
private static final int MULTIANEWARRAY = 197;
private static final int ALOAD_0 = 42;
private static final int WIDE = 196;
private static final int INVOKEINTERFACE = 185;
private static final int TABLESWITCH = 170;
private static final int IINC = 132;
private static final int LOOKUPSWITCH = 171;
private static final int endMarker = 255;
private final IntList bciRenumbering = new IntList();
private final Map labelsToOffsets = new HashMap();
private int byteCodeOffset;
private int renumberedOffset;
private final IntList bcLabelRelativeOffsets = new IntList();
public void setCurrentClass(String name, String superName) {
currentClass = name;
superClass = superName;
}
/**
* All input classes for the segment have now been read in, so this method
* is called so that this class can calculate/complete anything it could not
* do while classes were being read.
*/
public void finaliseBands() {
bcThisField = getIndexInClass(bcThisField);
bcThisMethod = getIndexInClass(bcThisMethod);
bcSuperMethod = getIndexInClass(bcSuperMethod);
bcInitRef = getIndexInClassForConstructor(bcInitRef);
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing byte code bands...");
byte[] encodedBand = encodeBandInt("bcCodes", bcCodes.toArray(),
Codec.BYTE1);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCodes["
+ bcCodes.size() + "]");
encodedBand = encodeBandInt("bcCaseCount", bcCaseCount.toArray(),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcCaseCount[" + bcCaseCount.size() + "]");
encodedBand = encodeBandInt("bcCaseValue", bcCaseValue.toArray(),
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcCaseValue[" + bcCaseValue.size() + "]");
encodedBand = encodeBandInt("bcByte", bcByte.toArray(), Codec.BYTE1);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcByte["
+ bcByte.size() + "]");
encodedBand = encodeBandInt("bcShort", bcShort.toArray(), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcShort["
+ bcShort.size() + "]");
encodedBand = encodeBandInt("bcLocal", bcLocal.toArray(),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLocal["
+ bcLocal.size() + "]");
encodedBand = encodeBandInt("bcLabel", integerListToArray(bcLabel),
Codec.BRANCH5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLabel["
+ bcLabel.size() + "]");
encodedBand = encodeBandInt("bcIntref", cpEntryListToArray(bcIntref),
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcIntref[" + bcIntref.size() + "]");
encodedBand = encodeBandInt("bcFloatRef",
cpEntryListToArray(bcFloatRef), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcFloatRef[" + bcFloatRef.size() + "]");
encodedBand = encodeBandInt("bcLongRef", cpEntryListToArray(bcLongRef),
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcLongRef[" + bcLongRef.size() + "]");
encodedBand = encodeBandInt("bcDoubleRef",
cpEntryListToArray(bcDoubleRef), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcDoubleRef[" + bcDoubleRef.size() + "]");
encodedBand = encodeBandInt("bcStringRef",
cpEntryListToArray(bcStringRef), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcStringRef[" + bcStringRef.size() + "]");
encodedBand = encodeBandInt("bcClassRef",
cpEntryOrNullListToArray(bcClassRef), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcClassRef[" + bcClassRef.size() + "]");
encodedBand = encodeBandInt("bcFieldRef",
cpEntryListToArray(bcFieldRef), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcFieldRef[" + bcFieldRef.size() + "]");
encodedBand = encodeBandInt("bcMethodRef",
cpEntryListToArray(bcMethodRef), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcMethodRef[" + bcMethodRef.size() + "]");
encodedBand = encodeBandInt("bcIMethodRef",
cpEntryListToArray(bcIMethodRef), Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcIMethodRef[" + bcIMethodRef.size() + "]");
encodedBand = encodeBandInt("bcThisField",
integerListToArray(bcThisField), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcThisField[" + bcThisField.size() + "]");
encodedBand = encodeBandInt("bcSuperField",
integerListToArray(bcSuperField), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcSuperField[" + bcSuperField.size() + "]");
encodedBand = encodeBandInt("bcThisMethod",
integerListToArray(bcThisMethod), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcThisMethod[" + bcThisMethod.size() + "]");
encodedBand = encodeBandInt("bcSuperMethod",
integerListToArray(bcSuperMethod), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcSuperMethod[" + bcSuperMethod.size() + "]");
encodedBand = encodeBandInt("bcInitRef", integerListToArray(bcInitRef),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from bcInitRef[" + bcInitRef.size() + "]");
// out.write(encodeBandInt(cpEntryintegerListToArray(bcEscRef),
// Codec.UNSIGNED5));
// out.write(encodeBandInt(integerListToArray(bcEscRefSize),
// Codec.UNSIGNED5));
// out.write(encodeBandInt(integerListToArray(bcEscSize),
// Codec.UNSIGNED5));
// out.write(encodeBandInt(integerListToArray(bcEscByte), Codec.BYTE1));
}
private List getIndexInClass(List cPMethodOrFieldList) {
List indices = new ArrayList(cPMethodOrFieldList.size());
for (int i = 0; i < cPMethodOrFieldList.size(); i++) {
CPMethodOrField cpMF = (CPMethodOrField) cPMethodOrFieldList.get(i);
indices.add(new Integer(cpMF.getIndexInClass()));
}
return indices;
}
private List getIndexInClassForConstructor(List cPMethodList) {
List indices = new ArrayList(cPMethodList.size());
for (int i = 0; i < cPMethodList.size(); i++) {
CPMethodOrField cpMF = (CPMethodOrField) cPMethodList.get(i);
indices.add(new Integer(cpMF.getIndexInClassForConstructor()));
}
return indices;
}
public void visitEnd() {
for (int i = 0; i < bciRenumbering.size(); i++) {
if (bciRenumbering.get(i) == -1) {
bciRenumbering.remove(i);
bciRenumbering.add(i, ++renumberedOffset);
}
}
if (renumberedOffset != 0) {
if(renumberedOffset + 1 != bciRenumbering.size()) {
throw new RuntimeException("Mistake made with renumbering");
}
for (int i = bcLabel.size() - 1; i >= 0; i--) {
Object label = bcLabel.get(i);
if (label instanceof Integer) {
break;
} else if (label instanceof Label) {
bcLabel.remove(i);
Integer offset = (Integer) labelsToOffsets.get(label);
int relativeOffset = bcLabelRelativeOffsets.get(i);
bcLabel.add(i, new Integer(bciRenumbering.get(offset.intValue()) - bciRenumbering.get(relativeOffset)));
}
}
bcCodes.add(endMarker);
segment.getClassBands().doBciRenumbering(bciRenumbering,
labelsToOffsets);
bciRenumbering.clear();
labelsToOffsets.clear();
byteCodeOffset = 0;
renumberedOffset = 0;
}
}
public void visitLabel(Label label) {
labelsToOffsets.put(label, new Integer(byteCodeOffset));
}
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
byteCodeOffset += 3;
updateRenumbering();
boolean aload_0 = false;
if (bcCodes.size() > 0
&& (bcCodes.get(bcCodes.size() - 1)) == ALOAD_0) {
bcCodes.remove(bcCodes.size() - 1);
aload_0 = true;
}
CPMethodOrField cpField = cpBands.getCPField(owner, name, desc);
if (aload_0) {
opcode += 7;
}
if (owner.equals(currentClass)) {
opcode += 24; // change to getstatic_this, putstatic_this etc.
bcThisField.add(cpField);
// } else if (owner.equals(superClass)) {
// opcode += 38; // change to getstatic_super etc.
// bcSuperField.add(cpField);
} else {
if (aload_0) {
opcode -= 7;
bcCodes.add(ALOAD_0); // add aload_0 back in because
// there's no special rewrite in
// this case.
}
bcFieldRef.add(cpField);
}
aload_0 = false;
bcCodes.add(opcode);
}
private void updateRenumbering() {
if(bciRenumbering.isEmpty()) {
bciRenumbering.add(0);
}
renumberedOffset ++;
for (int i = bciRenumbering.size(); i < byteCodeOffset; i++) {
bciRenumbering.add(-1);
}
bciRenumbering.add(renumberedOffset);
}
public void visitIincInsn(int var, int increment) {
if (var > 255 || increment > 255) {
byteCodeOffset += 6;
bcCodes.add(WIDE);
bcCodes.add(IINC);
bcLocal.add(var);
bcShort.add(increment);
} else {
byteCodeOffset += 3;
bcCodes.add(IINC);
bcLocal.add(var);
bcByte.add(increment & 0xFF);
}
updateRenumbering();
}
public void visitInsn(int opcode) {
if (opcode >= 202) {
throw new RuntimeException(
"Non-standard bytecode instructions not supported");
} else {
bcCodes.add(opcode);
byteCodeOffset++;
updateRenumbering();
}
}
public void visitIntInsn(int opcode, int operand) {
switch (opcode) {
case 17: // sipush
bcCodes.add(opcode);
bcShort.add(operand);
byteCodeOffset += 3;
break;
case 16: // bipush
case 188: // newarray
bcCodes.add(opcode);
bcByte.add(operand & 0xFF);
byteCodeOffset += 2;
}
updateRenumbering();
}
public void visitJumpInsn(int opcode, Label label) {
bcCodes.add(opcode);
bcLabel.add(label);
bcLabelRelativeOffsets.add(byteCodeOffset);
byteCodeOffset += 3;
updateRenumbering();
}
public void visitLdcInsn(Object cst) {
CPConstant constant = cpBands.getConstant(cst);
if (segment.lastConstantHadWideIndex() || constant instanceof CPLong
|| constant instanceof CPDouble) {
byteCodeOffset += 3;
if (constant instanceof CPInt) {
bcCodes.add(237); // ildc_w
bcIntref.add(constant);
} else if (constant instanceof CPFloat) {
bcCodes.add(238); // fldc
bcFloatRef.add(constant);
} else if (constant instanceof CPLong) {
bcCodes.add(20); // lldc2_w
bcLongRef.add(constant);
} else if (constant instanceof CPDouble) {
bcCodes.add(239); // dldc2_w
bcDoubleRef.add(constant);
} else if (constant instanceof CPString) {
bcCodes.add(19); // aldc
bcStringRef.add(constant);
} else if (constant instanceof CPClass) {
bcCodes.add(236); // cldc
bcClassRef.add(constant);
} else {
throw new RuntimeException("Constant should not be null");
}
} else {
byteCodeOffset += 2;
if (constant instanceof CPInt) {
bcCodes.add(234); // ildc
bcIntref.add(constant);
} else if (constant instanceof CPFloat) {
bcCodes.add(235); // fldc
bcFloatRef.add(constant);
} else if (constant instanceof CPString) {
bcCodes.add(18); // aldc
bcStringRef.add(constant);
} else if (constant instanceof CPClass) {
bcCodes.add(233); // cldc
bcClassRef.add(constant);
}
}
updateRenumbering();
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
bcCodes.add(LOOKUPSWITCH);
bcLabel.add(dflt);
bcLabelRelativeOffsets.add(byteCodeOffset);
bcCaseCount.add(keys.length);
for (int i = 0; i < labels.length; i++) {
bcCaseValue.add(keys[i]);
bcLabel.add(labels[i]);
bcLabelRelativeOffsets.add(byteCodeOffset);
}
int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - ((byteCodeOffset + 1) % 4);
byteCodeOffset += padding + 8 + 8 * keys.length;
updateRenumbering();
}
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
byteCodeOffset += 3;
updateRenumbering();
switch (opcode) {
case 182: // invokevirtual
case 183: // invokespecial
case 184: // invokestatic
boolean aload_0 = false;
if (bcCodes.size() > 0
&& (bcCodes.get(bcCodes.size() - 1))
== (ALOAD_0)) {
bcCodes.remove(bcCodes.size() - 1);
aload_0 = true;
opcode += 7;
}
if (owner.equals(currentClass)) {
opcode += 24; // change to invokevirtual_this,
// invokespecial_this etc.
if(name.equals("<init>") && opcode == 207) {
opcode = 230; // invokespecial_this_init
bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
} else {
bcThisMethod.add(cpBands.getCPMethod(owner, name, desc));
}
} else if (owner.equals(superClass)) { // TODO
opcode += 38; // change to invokevirtual_super,
// invokespecial_super etc.
if(name.equals("<init>") && opcode == 221) {
opcode = 231; // invokespecial_super_init
bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
} else {
bcSuperMethod.add(cpBands.getCPMethod(owner, name, desc));
}
} else {
if (aload_0) {
opcode -= 7;
bcCodes.add(ALOAD_0); // add aload_0 back in
// because there's no
// special rewrite in this
// case.
}
if(name.equals("<init>") && opcode == 183 && owner.equals(currentNewClass)) {
opcode = 232; // invokespecial_new_init
bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
} else {
bcMethodRef.add(cpBands.getCPMethod(owner, name, desc));
}
}
bcCodes.add(opcode);
break;
case 185: // invokeinterface
CPMethodOrField cpIMethod = cpBands.getCPIMethod(owner, name, desc);
bcIMethodRef.add(cpIMethod);
bcCodes.add(INVOKEINTERFACE);
break;
}
}
public void visitMultiANewArrayInsn(String desc, int dimensions) {
byteCodeOffset += 4;
updateRenumbering();
bcCodes.add(MULTIANEWARRAY);
bcClassRef.add(cpBands.getCPClass(desc));
bcByte.add(dimensions & 0xFF);
}
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label[] labels) {
bcCodes.add(TABLESWITCH);
bcLabel.add(dflt);
bcLabelRelativeOffsets.add(byteCodeOffset);
bcCaseValue.add(min);
int count = labels.length;
bcCaseCount.add(count);
for (int i = 0; i < count; i++) {
bcLabel.add(labels[i]);
bcLabelRelativeOffsets.add(byteCodeOffset);
}
int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - ((byteCodeOffset + 1) % 4);
byteCodeOffset+= (padding + 12 + 4 * labels.length);
updateRenumbering();
}
public void visitTypeInsn(int opcode, String type) {
// NEW, ANEWARRAY, CHECKCAST or INSTANCEOF
byteCodeOffset += 3;
updateRenumbering();
bcCodes.add(opcode);
bcClassRef.add(cpBands.getCPClass(type));
if(opcode == 187) { // NEW
currentNewClass = type;
}
}
public void visitVarInsn(int opcode, int var) {
// ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET
if (var > 255) {
byteCodeOffset += 4;
bcCodes.add(WIDE);
bcCodes.add(opcode);
bcLocal.add(var);
} else {
if(var > 3 || opcode == 169 /* RET */) {
byteCodeOffset += 2;
bcCodes.add(opcode);
bcLocal.add(var);
} else {
byteCodeOffset +=1;
switch(opcode) {
case 21: // ILOAD
case 54: // ISTORE
bcCodes.add(opcode + 5 + var);
break;
case 22: // LLOAD
case 55: // LSTORE
bcCodes.add(opcode + 8 + var);
break;
case 23: // FLOAD
case 56: // FSTORE
bcCodes.add(opcode + 11 + var);
break;
case 24: // DLOAD
case 57: // DSTORE
bcCodes.add(opcode + 14 + var);
break;
case 25: // A_LOAD
case 58: // A_STORE
bcCodes.add(opcode + 17 + var);
break;
}
}
}
updateRenumbering();
}
}

@ -0,0 +1,57 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a class
*/
public class CPClass extends CPConstant implements Comparable {
private final String className;
private final CPUTF8 utf8;
private final boolean isInnerClass;
public CPClass(CPUTF8 utf8) {
this.utf8 = utf8;
this.className = utf8.getUnderlyingString();
char[] chars = className.toCharArray();
for (int i = 0; i < chars.length; i++) {
if(chars[i] <= 0x2D) {
isInnerClass = true;
return;
}
}
isInnerClass = false;
}
public int compareTo(Object arg0) {
return className.compareTo(((CPClass)arg0).className);
}
public String toString() {
return className;
}
public int getIndexInCpUtf8() {
return utf8.getIndex();
}
public boolean isInnerClass() {
return isInnerClass;
}
}

@ -0,0 +1,26 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Abstract superclass for constant pool constant entries such as numbers or
* Strings
*/
public abstract class CPConstant extends ConstantPoolEntry implements
Comparable {
}

@ -0,0 +1,37 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a double.
*/
public class CPDouble extends CPConstant {
private final double theDouble;
public CPDouble(double theDouble) {
this.theDouble = theDouble;
}
public int compareTo(Object obj) {
return Double.compare(theDouble, ((CPDouble)obj).theDouble);
}
public double getDouble() {
return theDouble;
}
}

@ -0,0 +1,37 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a float.
*/
public class CPFloat extends CPConstant {
private final float theFloat;
public CPFloat(float theFloat) {
this.theFloat = theFloat;
}
public int compareTo(Object obj) {
return Float.compare(theFloat, ((CPFloat)obj).theFloat);
}
public float getFloat() {
return theFloat;
}
}

@ -0,0 +1,43 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for an int.
*/
public class CPInt extends CPConstant {
private final int theInt;
public CPInt(int theInt) {
this.theInt = theInt;
}
public int compareTo(Object obj) {
if(theInt > ((CPInt)obj).theInt) {
return 1;
} else if (theInt == ((CPInt)obj).theInt) {
return 0;
} else {
return -1;
}
}
public int getInt() {
return theInt;
}
}

@ -0,0 +1,48 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a long.
*/
public class CPLong extends CPConstant {
private final long theLong;
public CPLong(long theLong) {
this.theLong = theLong;
}
public int compareTo(Object obj) {
if(theLong > ((CPLong)obj).theLong) {
return 1;
} else if (theLong == ((CPLong)obj).theLong) {
return 0;
} else {
return -1;
}
}
public long getLong() {
return theLong;
}
public String toString() {
return "" + theLong;
}
}

@ -0,0 +1,95 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a method or field.
*/
public class CPMethodOrField extends ConstantPoolEntry implements Comparable {
private final CPClass className;
private final CPNameAndType nameAndType;
private int indexInClass = -1;
private int indexInClassForConstructor = -1;
public CPMethodOrField(CPClass className, CPNameAndType nameAndType) {
this.className = className;
this.nameAndType = nameAndType;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof CPMethodOrField)) {
return false;
}
return ((CPMethodOrField) obj).className.equals(className)
&& ((CPMethodOrField) obj).nameAndType.equals(nameAndType);
}
public int hashCode() {
return className.hashCode() + nameAndType.hashCode();
}
public String toString() {
return className + ": " + nameAndType;
}
public int compareTo(Object obj) {
if (obj instanceof CPMethodOrField) {
CPMethodOrField mof = (CPMethodOrField) obj;
int compareName = className.compareTo(mof.className);
if (compareName == 0) {
return nameAndType.compareTo(mof.nameAndType);
} else {
return compareName;
}
}
return 0;
}
public int getClassIndex() {
return className.getIndex();
}
public CPClass getClassName() {
return className;
}
public int getDescIndex() {
return nameAndType.getIndex();
}
public CPNameAndType getDesc() {
return nameAndType;
}
public int getIndexInClass() {
return indexInClass;
}
public void setIndexInClass(int index) {
indexInClass = index;
}
public int getIndexInClassForConstructor() {
return indexInClassForConstructor;
}
public void setIndexInClassForConstructor(int index) {
indexInClassForConstructor = index;
}
}

@ -0,0 +1,73 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a name and type pair.
*/
public class CPNameAndType extends ConstantPoolEntry implements Comparable {
private final CPUTF8 name;
private final CPSignature signature;
public CPNameAndType(CPUTF8 name, CPSignature signature) {
this.name = name;
this.signature = signature;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof CPNameAndType)) {
return false;
}
return ((CPNameAndType) obj).name.equals(name)
&& ((CPNameAndType) obj).signature.equals(signature);
}
public int hashCode() {
return name.hashCode() + signature.hashCode();
}
public String toString() {
return name + ":" + signature;
}
public int compareTo(Object obj) {
if (obj instanceof CPNameAndType) {
CPNameAndType nat = (CPNameAndType) obj;
int compareSignature = signature.compareTo(nat.signature);;
if(compareSignature == 0) {
return name.compareTo(nat.name);
} else {
return compareSignature;
}
}
return 0;
}
public int getNameIndex() {
return name.getIndex();
}
public String getName() {
return name.getUnderlyingString();
}
public int getTypeIndex() {
return signature.getIndex();
}
}

@ -0,0 +1,86 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.util.List;
/**
* Constant pool entry for a signature.
*/
public class CPSignature extends ConstantPoolEntry implements Comparable {
private final CPUTF8 signatureForm;
private final List classes;
private final String signature;
private final boolean formStartsWithBracket;
public CPSignature(String signature, CPUTF8 signatureForm, List classes) {
this.signature = signature;
this.signatureForm = signatureForm;
this.classes = classes;
formStartsWithBracket = signatureForm.toString().startsWith("(");
}
public int compareTo(Object arg0) {
if(signature.equals(((CPSignature) arg0).signature)) {
return 0;
}
if (formStartsWithBracket
&& !((CPSignature) arg0).formStartsWithBracket) {
return 1;
}
if (((CPSignature) arg0).formStartsWithBracket
&& !formStartsWithBracket) {
return -1;
}
if (classes.size() - ((CPSignature) arg0).classes.size() != 0) {
return classes.size() - ((CPSignature) arg0).classes.size();
}
if (classes.size() > 0) {
for (int i = classes.size() - 1; i >=0; i--) {
CPClass cpClass = (CPClass) classes.get(i);
CPClass compareClass = (CPClass) ((CPSignature) arg0).classes
.get(i);
int classComp = cpClass.compareTo(compareClass);
if(classComp != 0) {
return classComp;
}
}
}
return signature.compareTo(((CPSignature) arg0).signature);
}
public int getIndexInCpUtf8() {
return signatureForm.getIndex();
}
public List getClasses() {
return classes;
}
public String toString() {
return signature;
}
public String getUnderlyingString() {
return signature;
}
public CPUTF8 getSignatureForm() {
return signatureForm;
}
}

@ -0,0 +1,44 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a String.
*/
public class CPString extends CPConstant {
private final String string;
private final CPUTF8 utf8;
public CPString(CPUTF8 utf8) {
this.utf8 = utf8;
this.string = utf8.getUnderlyingString();
}
public int compareTo(Object arg0) {
return string.compareTo(((CPString)arg0).string);
}
public String toString() {
return string;
}
public int getIndexInCpUtf8() {
return utf8.getIndex();
}
}

@ -0,0 +1,42 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Constant pool entry for a UTF8 entry, used for storing long Strings.
*/
public class CPUTF8 extends ConstantPoolEntry implements Comparable {
private final String string;
public CPUTF8(String string) {
this.string = string;
}
public int compareTo(Object arg0) {
return string.compareTo(((CPUTF8)arg0).string);
}
public String toString() {
return string;
}
public String getUnderlyingString() {
return string;
}
}

@ -0,0 +1,210 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Sets of codecs that share characteristics. Mainly used for different effort
* compression heuristics in BandSet.
*/
public class CanonicalCodecFamilies {
// Families of codecs for bands of positive integers that do not correlate
// well (i.e. would not benefit from delta encoding)
public static BHSDCodec[] nonDeltaUnsignedCodecs1 = new BHSDCodec[] {
// (1,256) is a special case and is considered separately so shouldn't be included here
// CodecEncoding.getCanonicalCodec(1), // (1,256)
CodecEncoding.getCanonicalCodec(5), // (2,256)
CodecEncoding.getCanonicalCodec(9), // (3,256)
CodecEncoding.getCanonicalCodec(13) // (4,256)
};
public static BHSDCodec[] nonDeltaUnsignedCodecs2 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(17), // (5,4)
CodecEncoding.getCanonicalCodec(20), // (5,16)
CodecEncoding.getCanonicalCodec(23), // (5,32)
CodecEncoding.getCanonicalCodec(26), // (5,64)
CodecEncoding.getCanonicalCodec(29) // (5,128)
};
public static BHSDCodec[] nonDeltaUnsignedCodecs3 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(47), // (2,192)
CodecEncoding.getCanonicalCodec(48), // (2,224)
CodecEncoding.getCanonicalCodec(49), // (2,240)
CodecEncoding.getCanonicalCodec(50), // (2,248)
CodecEncoding.getCanonicalCodec(51) // (2,252)
};
public static BHSDCodec[] nonDeltaUnsignedCodecs4 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(70), // (3,192)
CodecEncoding.getCanonicalCodec(71), // (3,224)
CodecEncoding.getCanonicalCodec(72), // (3,240)
CodecEncoding.getCanonicalCodec(73), // (3,248)
CodecEncoding.getCanonicalCodec(74) // (3,252)
};
public static BHSDCodec[] nonDeltaUnsignedCodecs5 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(93), // (4,192)
CodecEncoding.getCanonicalCodec(94), // (4,224)
CodecEncoding.getCanonicalCodec(95), // (4,240)
CodecEncoding.getCanonicalCodec(96), // (4,248)
CodecEncoding.getCanonicalCodec(97) // (4,252)
};
// Families of codecs for bands of positive integers that do correlate well
// and would benefit from delta encoding
public static BHSDCodec[] deltaUnsignedCodecs1 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(3), // (1,256,0,1)
CodecEncoding.getCanonicalCodec(7), // (2,256,0,1)
CodecEncoding.getCanonicalCodec(11), // (3,256,0,1)
CodecEncoding.getCanonicalCodec(15) // (4,256,0,1)
};
public static BHSDCodec[] deltaUnsignedCodecs2 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(32), // (5,4,0,1)
CodecEncoding.getCanonicalCodec(35), // (5,16,0,1)
CodecEncoding.getCanonicalCodec(38), // (5,32,0,1)
CodecEncoding.getCanonicalCodec(41), // (5,64,0,1)
CodecEncoding.getCanonicalCodec(44) // (5,128,0,1)
};
public static BHSDCodec[] deltaUnsignedCodecs3 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(52), // (2,8,0,1)
CodecEncoding.getCanonicalCodec(54), // (2,16,0,1)
CodecEncoding.getCanonicalCodec(56), // (2,32,0,1)
CodecEncoding.getCanonicalCodec(58), // (2,64,0,1)
CodecEncoding.getCanonicalCodec(60), // (2,128,0,1)
CodecEncoding.getCanonicalCodec(62), // (2,192,0,1)
CodecEncoding.getCanonicalCodec(64), // (2,224,0,1)
CodecEncoding.getCanonicalCodec(66), // (2,240,0,1)
CodecEncoding.getCanonicalCodec(68) // (2,248,0,1)
};
public static BHSDCodec[] deltaUnsignedCodecs4 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(75), // (3,8,0,1)
CodecEncoding.getCanonicalCodec(77), // (3,16,0,1)
CodecEncoding.getCanonicalCodec(79), // (3,32,0,1)
CodecEncoding.getCanonicalCodec(81), // (3,64,0,1)
CodecEncoding.getCanonicalCodec(83), // (3,128,0,1)
CodecEncoding.getCanonicalCodec(85), // (3,192,0,1)
CodecEncoding.getCanonicalCodec(87), // (3,224,0,1)
CodecEncoding.getCanonicalCodec(89), // (3,240,0,1)
CodecEncoding.getCanonicalCodec(91) // (3,248,0,1)
};
public static BHSDCodec[] deltaUnsignedCodecs5 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(98), // (4,8,0,1)
CodecEncoding.getCanonicalCodec(100), // (4,16,0,1)
CodecEncoding.getCanonicalCodec(102), // (4,32,0,1)
CodecEncoding.getCanonicalCodec(104), // (4,64,0,1)
CodecEncoding.getCanonicalCodec(106), // (4,128,0,1)
CodecEncoding.getCanonicalCodec(108), // (4,192,0,1)
CodecEncoding.getCanonicalCodec(110), // (4,224,0,1)
CodecEncoding.getCanonicalCodec(112), // (4,240,0,1)
CodecEncoding.getCanonicalCodec(114) // (4,248,0,1)
};
// Families of codecs for bands containing positive and negative integers
// that do correlate well (i.e. delta encoding is used)
public static BHSDCodec[] deltaSignedCodecs1 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(4), // (1,256,1,1)
CodecEncoding.getCanonicalCodec(8), // (2,256,1,1)
CodecEncoding.getCanonicalCodec(12), // (3,256,1,1)
CodecEncoding.getCanonicalCodec(16) // (4,256,1,1)
};
public static BHSDCodec[] deltaSignedCodecs2 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(33), // (5,4,1,1)
CodecEncoding.getCanonicalCodec(36), // (5,16,1,1)
CodecEncoding.getCanonicalCodec(39), // (5,32,1,1)
CodecEncoding.getCanonicalCodec(42), // (5,64,1,1)
CodecEncoding.getCanonicalCodec(45) // (5,128,1,1)
};
public static BHSDCodec[] deltaSignedCodecs3 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(53), // (2,8,1,1)
CodecEncoding.getCanonicalCodec(55), // (2,16,1,1)
CodecEncoding.getCanonicalCodec(57), // (2,32,1,1)
CodecEncoding.getCanonicalCodec(59), // (2,64,1,1)
CodecEncoding.getCanonicalCodec(61), // (2,128,1,1)
CodecEncoding.getCanonicalCodec(63), // (2,192,1,1)
CodecEncoding.getCanonicalCodec(65), // (2,224,1,1)
CodecEncoding.getCanonicalCodec(67), // (2,240,1,1)
CodecEncoding.getCanonicalCodec(69) // (2,248,1,1)
};
public static BHSDCodec[] deltaSignedCodecs4 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(76), // (3,8,1,1)
CodecEncoding.getCanonicalCodec(78), // (3,16,1,1)
CodecEncoding.getCanonicalCodec(80), // (3,32,1,1)
CodecEncoding.getCanonicalCodec(82), // (3,64,1,1)
CodecEncoding.getCanonicalCodec(84), // (3,128,1,1)
CodecEncoding.getCanonicalCodec(86), // (3,192,1,1)
CodecEncoding.getCanonicalCodec(88), // (3,224,1,1)
CodecEncoding.getCanonicalCodec(90), // (3,240,1,1)
CodecEncoding.getCanonicalCodec(92) // (3,248,1,1)
};
public static BHSDCodec[] deltaSignedCodecs5 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(99), // (4,8,1,1)
CodecEncoding.getCanonicalCodec(101), // (4,16,1,1)
CodecEncoding.getCanonicalCodec(103), // (4,32,1,1)
CodecEncoding.getCanonicalCodec(105), // (4,64,1,1)
CodecEncoding.getCanonicalCodec(107), // (4,128,1,1)
CodecEncoding.getCanonicalCodec(109), // (4,192,1,1)
CodecEncoding.getCanonicalCodec(111), // (4,224,1,1)
CodecEncoding.getCanonicalCodec(113), // (4,240,1,1)
CodecEncoding.getCanonicalCodec(115) // (4,248,1,1)
};
public static BHSDCodec[] deltaDoubleSignedCodecs1 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(34), // (5,4,2,1)
CodecEncoding.getCanonicalCodec(37), // (5,16,2,1)
CodecEncoding.getCanonicalCodec(40), // (5,32,2,1)
CodecEncoding.getCanonicalCodec(43), // (5,64,2,1)
CodecEncoding.getCanonicalCodec(46) // (5,128,2,1)
};
// Families of codecs for bands containing positive and negative values that
// do not correlate well (i.e. delta encoding is not used)
public static BHSDCodec[] nonDeltaSignedCodecs1 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(2), // (1,256,1)
CodecEncoding.getCanonicalCodec(6), // (2,256,1)
CodecEncoding.getCanonicalCodec(10), // (3,256,1)
CodecEncoding.getCanonicalCodec(14) // (4,256,1)
};
public static BHSDCodec[] nonDeltaSignedCodecs2 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(18), // (5,4,1)
CodecEncoding.getCanonicalCodec(21), // (5,16,1)
CodecEncoding.getCanonicalCodec(24), // (5,32,1)
CodecEncoding.getCanonicalCodec(27), // (5,64,1)
CodecEncoding.getCanonicalCodec(30) // (5,128,1)
};
public static BHSDCodec[] nonDeltaDoubleSignedCodecs1 = new BHSDCodec[] {
CodecEncoding.getCanonicalCodec(19), // (5,4,2)
CodecEncoding.getCanonicalCodec(22), // (5,16,2)
CodecEncoding.getCanonicalCodec(25), // (5,32,2)
CodecEncoding.getCanonicalCodec(28), // (5,64,2)
CodecEncoding.getCanonicalCodec(31) // (5,128,2)
};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,246 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
/**
* A Codec allows a sequence of bytes to be decoded into integer values (or vice
* versa).
*
* There are a number of standard Codecs ({@link #UDELTA5}, {@link #UNSIGNED5},
* {@link #BYTE1}, {@link #CHAR3}) that are used in the implementation of many
* bands; but there are a variety of other ones, and indeed the specification
* assumes that other combinations of values can result in more specific and
* efficient formats. There are also a sequence of canonical encodings defined
* by the Pack200 specification, which allow a Codec to be referred to by
* canonical number. {@link CodecEncoding#getCodec(int, InputStream, Codec)})
*/
public abstract class Codec {
/**
* BCI5 = (5,4): Used for storing branching information in bytecode.
*/
public static final BHSDCodec BCI5 = new BHSDCodec(5, 4);
/**
* BRANCH5 = (5,4,2): Used for storing branching information in bytecode.
*/
public static final BHSDCodec BRANCH5 = new BHSDCodec(5, 4, 2);
/**
* BYTE1 = (1,256): Used for storing plain bytes.
*/
public static final BHSDCodec BYTE1 = new BHSDCodec(1, 256);
/**
* CHAR3 = (3,128): Used for storing text (UTF-8) strings. NB This isn't
* quite the same as UTF-8, but has similar properties; ASCII characters
* &lt; 127 are stored in a single byte.
*/
public static final BHSDCodec CHAR3 = new BHSDCodec(3, 128);
/**
* DELTA5 = (5,64,1,1): Used for the majority of numerical codings where
* there is a correlated sequence of signed values.
*/
public static final BHSDCodec DELTA5 = new BHSDCodec(5, 64, 1, 1);
/**
* MDELTA5 = (5,64,2,1): Used for the majority of numerical codings where
* there is a correlated sequence of signed values, but where most of them
* are expected to be non-negative.
*/
public static final BHSDCodec MDELTA5 = new BHSDCodec(5, 64, 2, 1);
/**
* SIGNED5 = (5,64,1): Used for small signed values.
*/
public static final BHSDCodec SIGNED5 = new BHSDCodec(5, 64, 1);
/**
* UDELTA5 = (5,64,0,1): Used for the majority of numerical codings where
* there is a correlated sequence of unsigned values.
*/
public static final BHSDCodec UDELTA5 = new BHSDCodec(5, 64, 0, 1);
/**
* UNSIGNED5 = (5,64): Used for small unsigned values.
*/
public static final BHSDCodec UNSIGNED5 = new BHSDCodec(5, 64);
public int lastBandLength;
/**
* Decode a sequence of bytes from the given input stream, returning the
* value as a long. Note that this method can only be applied for non-delta
* encodings.
*
* @param in
* the input stream to read from
* @return the value as a long
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if the encoding is a delta encoding
*/
public abstract int decode(InputStream in) throws IOException,
Pack200Exception;
/**
* Encode a single value into a sequence of bytes.
*
* @param value
* the value to encode
* @param last
* the previous value encoded (for delta encodings)
* @return the encoded bytes
* @throws Pack200Exception
*/
public abstract byte[] encode(int value, int last)
throws Pack200Exception;
/**
* Encode a single value into a sequence of bytes. Note that this method can
* only be used for non-delta encodings.
*
* @param value
* the value to encode
* @return the encoded bytes
* @throws Pack200Exception
*/
public abstract byte[] encode(int value) throws Pack200Exception;
/**
* Decode a sequence of bytes from the given input stream, returning the
* value as a long. If this encoding is a delta encoding (d=1) then the
* previous value must be passed in as a parameter. If it is a non-delta
* encoding, then it does not matter what value is passed in, so it makes
* sense for the value to be passed in by default using code similar to:
*
* <pre>
* long last = 0;
* while (condition) {
* last = codec.decode(in, last);
* // do something with last
* }
* </pre>
*
* @param in
* the input stream to read from
* @param last
* the previous value read, which must be supplied if the codec
* is a delta encoding
* @return the value as a long
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
public abstract int decode(InputStream in, long last) throws IOException,
Pack200Exception;
/**
* Decodes a sequence of <code>n</code> values from <code>in</code>.
* This should probably be used in most cases, since some codecs (such as
*
* @{link PopCodec}) only work when the number of values to be read is
* known.
*
* @param n
* the number of values to decode
* @param in
* the input stream to read from
* @return an array of <code>int</code> values corresponding to values
* decoded
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
public int[] decodeInts(int n, InputStream in) throws IOException,
Pack200Exception {
lastBandLength = 0;
int result[] = new int[n];
int last = 0;
for (int i = 0; i < n; i++) {
result[i] = last = decode(in, last);
}
return result;
}
/**
* Decodes a sequence of <code>n</code> values from <code>in</code>.
*
* @param n
* the number of values to decode
* @param in
* the input stream to read from
* @param firstValue
* the first value in the band if it has already been read
* @return an array of <code>int</code> values corresponding to values
* decoded, with firstValue as the first value in the array.
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
public int[] decodeInts(int n, InputStream in, int firstValue)
throws IOException, Pack200Exception {
int result[] = new int[n + 1];
result[0] = firstValue;
int last = firstValue;
for (int i = 1; i < n + 1; i++) {
result[i] = last = decode(in, last);
}
return result;
}
/**
* Encode a sequence of integers into a byte array
*
* @param ints
* the values to encode
* @return byte[] encoded bytes
* @throws Pack200Exception
* if there is a problem encoding any of the values
*/
public byte[] encode(int[] ints) throws Pack200Exception {
int total = 0;
byte[][] bytes = new byte[ints.length][];
for (int i = 0; i < ints.length; i++) {
bytes[i] = encode(ints[i], i > 0 ? ints[i-1] : 0);
total += bytes[i].length;
}
byte[] encoded = new byte[total];
int index = 0;
for (int i = 0; i < bytes.length; i++) {
System.arraycopy(bytes[i], 0, encoded, index, bytes[i].length);
index += bytes[i].length;
}
return encoded;
}
}

@ -0,0 +1,346 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* CodecEncoding is used to get the right Codec for a given meta-encoding
*/
public class CodecEncoding {
/**
* The canonical encodings are defined to allow a single byte to represent
* one of the standard encodings. The following values are defined in the
* Pack200 specification, and this array cannot be changed.
*/
private static final BHSDCodec[] canonicalCodec = { null,
new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1),
new BHSDCodec(1, 256, 0, 1), new BHSDCodec(1, 256, 1, 1),
new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1),
new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1),
new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1),
new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1),
new BHSDCodec(4, 256), new BHSDCodec(4, 256, 1),
new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1),
new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16),
new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2),
new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1),
new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64),
new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2),
new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1),
new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1),
new BHSDCodec(5, 16, 0, 1), new BHSDCodec(5, 16, 1, 1),
new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1),
new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1),
new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1),
new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1),
new BHSDCodec(5, 128, 1, 1), new BHSDCodec(5, 128, 2, 1),
new BHSDCodec(2, 192), new BHSDCodec(2, 224),
new BHSDCodec(2, 240), new BHSDCodec(2, 248),
new BHSDCodec(2, 252), new BHSDCodec(2, 8, 0, 1),
new BHSDCodec(2, 8, 1, 1), new BHSDCodec(2, 16, 0, 1),
new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1),
new BHSDCodec(2, 32, 1, 1), new BHSDCodec(2, 64, 0, 1),
new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1),
new BHSDCodec(2, 128, 1, 1), new BHSDCodec(2, 192, 0, 1),
new BHSDCodec(2, 192, 1, 1), new BHSDCodec(2, 224, 0, 1),
new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1),
new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192),
new BHSDCodec(3, 224), new BHSDCodec(3, 240),
new BHSDCodec(3, 248), new BHSDCodec(3, 252),
new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1),
new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1),
new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1),
new BHSDCodec(3, 64, 0, 1), new BHSDCodec(3, 64, 1, 1),
new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1),
new BHSDCodec(3, 192, 0, 1), new BHSDCodec(3, 192, 1, 1),
new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1),
new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1),
new BHSDCodec(3, 248, 0, 1), new BHSDCodec(3, 248, 1, 1),
new BHSDCodec(4, 192), new BHSDCodec(4, 224),
new BHSDCodec(4, 240), new BHSDCodec(4, 248),
new BHSDCodec(4, 252), new BHSDCodec(4, 8, 0, 1),
new BHSDCodec(4, 8, 1, 1), new BHSDCodec(4, 16, 0, 1),
new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1),
new BHSDCodec(4, 32, 1, 1), new BHSDCodec(4, 64, 0, 1),
new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1),
new BHSDCodec(4, 128, 1, 1), new BHSDCodec(4, 192, 0, 1),
new BHSDCodec(4, 192, 1, 1), new BHSDCodec(4, 224, 0, 1),
new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1),
new BHSDCodec(4, 248, 1, 1) };
private static Map canonicalCodecsToSpecifiers;
/**
* Returns the codec specified by the given value byte and optional byte
* header. If the value is >=116, then bytes may be consumed from the
* secondary input stream, which is taken to be the contents of the
* band_headers byte array. Since the values from this are consumed and not
* repeated, the input stream should be reused for subsequent encodings.
* This does not therefore close the input stream.
*
* @param value
* the canonical encoding value
* @param in
* the input stream to read additional byte headers from
* @param defaultCodec
* TODO
* @return the corresponding codec, or <code>null</code> if the default
* should be used
*
* @throws IOException
* if there is a problem reading from the input stream (which in
* reality, is never, since the band_headers are likely stored
* in a byte array and accessed via a ByteArrayInputStream.
* However, an EOFException could occur if things go wrong)
* @throws Pack200Exception
*/
public static Codec getCodec(int value, InputStream in, Codec defaultCodec)
throws IOException, Pack200Exception {
// Sanity check to make sure that no-one has changed
// the canonical codecs, which would really cause havoc
if (canonicalCodec.length != 116) {
throw new Error(
"Canonical encodings have been incorrectly modified");
}
if (value < 0) {
throw new IllegalArgumentException(
"Encoding cannot be less than zero");
} else if (value == 0) {
return defaultCodec;
} else if (value <= 115) {
return canonicalCodec[value];
} else if (value == 116) {
int code = in.read();
if (code == -1) {
throw new EOFException(
"End of buffer read whilst trying to decode codec");
}
int d = (code & 0x01);
int s = (code >> 1 & 0x03);
int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
// number, but it's checked in the
// Codec constructor
code = in.read();
if (code == -1) {
throw new EOFException(
"End of buffer read whilst trying to decode codec");
}
int h = code + 1;
// This handles the special cases for invalid combinations of data.
return new BHSDCodec(b, h, s, d);
} else if (value >= 117 && value <= 140) { // Run codec
int offset = value - 117;
int kx = offset & 3;
boolean kbflag = (offset >> 2 & 1) == 1;
boolean adef = (offset >> 3 & 1) == 1;
boolean bdef = (offset >> 4 & 1) == 1;
// If both A and B use the default encoding, what's the point of
// having a run of default values followed by default values
if (adef && bdef) {
throw new Pack200Exception(
"ADef and BDef should never both be true");
}
int kb = (kbflag ? in.read() : 3);
int k = (kb + 1) * (int) Math.pow(16, kx);
Codec aCodec, bCodec;
if (adef) {
aCodec = defaultCodec;
} else {
aCodec = getCodec(in.read(), in, defaultCodec);
}
if (bdef) {
bCodec = defaultCodec;
} else {
bCodec = getCodec(in.read(), in, defaultCodec);
}
return new RunCodec(k, aCodec, bCodec);
} else if (value >= 141 && value <= 188) { // Population Codec
int offset = value - 141;
boolean fdef = (offset & 1) == 1;
boolean udef = (offset >> 1 & 1) == 1;
int tdefl = offset >> 2;
boolean tdef = tdefl != 0;
// From section 6.7.3 of spec
final int[] tdefToL = { 0, 4, 8, 16, 32, 64, 128, 192, 224, 240,
248, 252 };
int l = tdefToL[tdefl];
// NOTE: Do not re-factor this to bring out uCodec; the order in
// which
// they are read from the stream is important
if (tdef) {
Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
defaultCodec));
Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
defaultCodec));
// Unfortunately, if tdef, then tCodec depends both on l and
// also on k, the
// number of items read from the fCodec. So we don't know in
// advance what
// the codec will be.
return new PopulationCodec(fCodec, l, uCodec);
} else {
Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
defaultCodec));
Codec tCodec = getCodec(in.read(), in, defaultCodec);
Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
defaultCodec));
return new PopulationCodec(fCodec, tCodec, uCodec);
}
} else {
throw new Pack200Exception("Invalid codec encoding byte (" + value
+ ") found");
}
}
public static int getSpecifierForDefaultCodec(BHSDCodec defaultCodec) {
return getSpecifier(defaultCodec, null)[0];
}
public static int[] getSpecifier(Codec codec, Codec defaultForBand) {
// lazy initialization
if(canonicalCodecsToSpecifiers == null) {
HashMap reverseMap = new HashMap(canonicalCodec.length);
for (int i = 0; i < canonicalCodec.length; i++) {
reverseMap.put(canonicalCodec[i], new Integer(i));
}
canonicalCodecsToSpecifiers = reverseMap;
}
if(canonicalCodecsToSpecifiers.containsKey(codec)) {
return new int[] {((Integer)canonicalCodecsToSpecifiers.get(codec)).intValue()};
} else if (codec instanceof BHSDCodec) {
// Cache these?
BHSDCodec bhsdCodec = (BHSDCodec)codec;
int[] specifiers = new int[3];
specifiers[0] = 116;
specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2
* bhsdCodec.getS() + 8 * (bhsdCodec.getB()-1);
specifiers[2] = bhsdCodec.getH() - 1;
return specifiers;
} else if (codec instanceof RunCodec) {
RunCodec runCodec = (RunCodec) codec;
int k = runCodec.getK();
int kb;
int kx;
if(k <= 256) {
kb = 0;
kx = k - 1;
} else if (k <= 4096) {
kb = 1;
kx = k/16 - 1;
} else if (k <= 65536) {
kb = 2;
kx = k/256 - 1;
} else {
kb = 3;
kx = k/4096 - 1;
}
Codec aCodec = runCodec.getACodec();
Codec bCodec = runCodec.getBCodec();
int abDef = 0;
if(aCodec.equals(defaultForBand)) {
abDef = 1;
} else if (bCodec.equals(defaultForBand)) {
abDef = 2;
}
int first = 117 + kb + (kx==3 ? 0 : 4) + (8 * abDef);
int[] aSpecifier = abDef == 1 ? new int[0] : getSpecifier(aCodec, defaultForBand);
int[] bSpecifier = abDef == 2 ? new int[0] : getSpecifier(bCodec, defaultForBand);
int[] specifier = new int[1 + (kx==3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
specifier[0] = first;
int index = 1;
if(kx != 3) {
specifier[1] = kx;
index++;
}
for (int i = 0; i < aSpecifier.length; i++) {
specifier[index] = aSpecifier[i];
index++;
}
for (int i = 0; i < bSpecifier.length; i++) {
specifier[index] = bSpecifier[i];
index++;
}
return specifier;
} else if (codec instanceof PopulationCodec) {
PopulationCodec populationCodec = (PopulationCodec) codec;
Codec tokenCodec = populationCodec.getTokenCodec();
Codec favouredCodec = populationCodec.getFavouredCodec();
Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
int tDefL = 0;
int[] favoured = populationCodec.getFavoured();
if(favoured != null) {
int k = favoured.length;
if(tokenCodec == Codec.BYTE1) {
tDefL = 1;
} else if (tokenCodec instanceof BHSDCodec) {
BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
if(tokenBHSD.getS() == 0) {
int[] possibleLValues = new int[] {4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252};
int l = 256-tokenBHSD.getH();
int index = Arrays.binarySearch(possibleLValues, l);
if(index != -1) {
// TODO: check range is ok for ks
tDefL = index++;
}
}
}
}
int first = 141 + fDef + (2 * uDef) + (4 * tDefL);
int[] favouredSpecifier = fDef == 1 ? new int[0] : getSpecifier(favouredCodec, defaultForBand);
int[] tokenSpecifier = tDefL != 0 ? new int[0] : getSpecifier(tokenCodec, defaultForBand);
int[] unfavouredSpecifier = uDef == 1 ? new int[0] : getSpecifier(unfavouredCodec, defaultForBand);
int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length + tokenSpecifier.length];
specifier[0] = first;
int index = 1;
for (int i = 0; i < favouredSpecifier.length; i++) {
specifier[index] = favouredSpecifier[i];
index++;
}
for (int i = 0; i < tokenSpecifier.length; i++) {
specifier[index] = tokenSpecifier[i];
index++;
}
for (int i = 0; i < unfavouredSpecifier.length; i++) {
specifier[index] = unfavouredSpecifier[i];
index++;
}
return specifier;
}
return null;
}
public static BHSDCodec getCanonicalCodec(int i) {
return canonicalCodec[i];
}
}

@ -0,0 +1,34 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Abstract superclass for constant pool entries
*/
public abstract class ConstantPoolEntry {
private int index = -1;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

@ -0,0 +1,694 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.objectweb.asm.Type;
/**
* Pack200 Constant Pool Bands
*/
public class CpBands extends BandSet {
// Don't need to include default attribute names in the constant pool bands
private final Set defaultAttributeNames = new HashSet();
private final Set cp_Utf8 = new TreeSet();
private final Set cp_Int = new TreeSet();
private final Set cp_Float = new TreeSet();
private final Set cp_Long = new TreeSet();
private final Set cp_Double = new TreeSet();
private final Set cp_String = new TreeSet();
private final Set cp_Class = new TreeSet();
private final Set cp_Signature = new TreeSet();
private final Set cp_Descr = new TreeSet();
private final Set cp_Field = new TreeSet();
private final Set cp_Method = new TreeSet();
private final Set cp_Imethod = new TreeSet();
private final Map stringsToCpUtf8 = new HashMap();
private final Map stringsToCpNameAndType = new HashMap();
private final Map stringsToCpClass = new HashMap();
private final Map stringsToCpSignature = new HashMap();
private final Map stringsToCpMethod = new HashMap();
private final Map stringsToCpField = new HashMap();
private final Map stringsToCpIMethod = new HashMap();
private final Map objectsToCPConstant = new HashMap();
private final Segment segment;
public CpBands(Segment segment, int effort) {
super(effort, segment.getSegmentHeader());
this.segment = segment;
defaultAttributeNames.add("AnnotationDefault");
defaultAttributeNames.add("RuntimeVisibleAnnotations");
defaultAttributeNames.add("RuntimeInvisibleAnnotations");
defaultAttributeNames.add("RuntimeVisibleParameterAnnotations");
defaultAttributeNames.add("RuntimeInvisibleParameterAnnotations");
defaultAttributeNames.add("Code");
defaultAttributeNames.add("LineNumberTable");
defaultAttributeNames.add("LocalVariableTable");
defaultAttributeNames.add("LocalVariableTypeTable");
defaultAttributeNames.add("ConstantValue");
defaultAttributeNames.add("Deprecated");
defaultAttributeNames.add("EnclosingMethod");
defaultAttributeNames.add("Exceptions");
defaultAttributeNames.add("InnerClasses");
defaultAttributeNames.add("Signature");
defaultAttributeNames.add("SourceFile");
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing constant pool bands...");
writeCpUtf8(out);
writeCpInt(out);
writeCpFloat(out);
writeCpLong(out);
writeCpDouble(out);
writeCpString(out);
writeCpClass(out);
writeCpSignature(out);
writeCpDescr(out);
writeCpMethodOrField(cp_Field, out, "cp_Field");
writeCpMethodOrField(cp_Method, out, "cp_Method");
writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
}
private void writeCpUtf8(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
List chars = new ArrayList();
List bigSuffix = new ArrayList();
List bigChars = new ArrayList();
Object[] cpUtf8Array = cp_Utf8.toArray();
String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
cpUtf8Suffix[0] = first.length();
addCharacters(chars, first.toCharArray());
for (int i = 2; i < cpUtf8Array.length; i++) {
char[] previous = ((CPUTF8) cpUtf8Array[i - 1])
.getUnderlyingString().toCharArray();
String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
char[] current = currentStr.toCharArray();
int prefix = 0;
for (int j = 0; j < previous.length; j++) {
if (previous[j] == current[j]) {
prefix++;
} else {
break;
}
}
cpUtf8Prefix[i - 2] = prefix;
currentStr = currentStr.substring(prefix);
char[] suffix = currentStr.toCharArray();
if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
// do better?)
cpUtf8Suffix[i - 1] = 0;
bigSuffix.add(new Integer(suffix.length));
addCharacters(bigChars, suffix);
} else {
cpUtf8Suffix[i - 1] = suffix.length;
addCharacters(chars, suffix);
}
}
int[] cpUtf8Chars = new int[chars.size()];
int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
for (int i = 0; i < cpUtf8Chars.length; i++) {
cpUtf8Chars[i] = ((Character) chars.get(i)).charValue();
}
for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
int numBigChars = ((Integer) bigSuffix.get(i)).intValue();
cpUtf8BigSuffix[i] = numBigChars;
cpUtf8BigChars[i] = new int[numBigChars];
for (int j = 0; j < numBigChars; j++) {
cpUtf8BigChars[i][j] = ((Character) bigChars.remove(0)).charValue();
}
}
byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix,
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
for (int i = 0; i < cpUtf8BigChars.length; i++) {
encodedBand = encodeBandInt("cpUtf8BigChars " + i,
cpUtf8BigChars[i], Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpUtf8BigChars" + i + "["
+ cpUtf8BigChars[i].length + "]");
}
}
private void addCharacters(List chars, char[] charArray) {
for (int i = 0; i < charArray.length; i++) {
chars.add(new Character(charArray[i]));
}
}
private void writeCpInt(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
int[] cpInt = new int[cp_Int.size()];
int i = 0;
for (Iterator iterator = cp_Int.iterator(); iterator.hasNext();) {
CPInt integer = (CPInt) iterator.next();
cpInt[i] = integer.getInt();
i++;
}
byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Int[" + cpInt.length + "]");
}
private void writeCpFloat(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
int[] cpFloat = new int[cp_Float.size()];
int i = 0;
for (Iterator iterator = cp_Float.iterator(); iterator.hasNext();) {
CPFloat fl = (CPFloat) iterator.next();
cpFloat[i] = Float.floatToIntBits(fl.getFloat());
i++;
}
byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Float[" + cpFloat.length + "]");
}
private void writeCpLong(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
int[] highBits = new int[cp_Long.size()];
int[] loBits = new int[cp_Long.size()];
int i = 0;
for (Iterator iterator = cp_Long.iterator(); iterator.hasNext();) {
CPLong lng = (CPLong) iterator.next();
long l = lng.getLong();
highBits[i] = (int) (l >> 32);
loBits[i] = (int) l;
i++;
}
byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Long_hi[" + highBits.length + "]");
encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Long_lo[" + loBits.length + "]");
}
private void writeCpDouble(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
int[] highBits = new int[cp_Double.size()];
int[] loBits = new int[cp_Double.size()];
int i = 0;
for (Iterator iterator = cp_Double.iterator(); iterator.hasNext();) {
CPDouble dbl = (CPDouble) iterator.next();
long l = Double.doubleToLongBits(dbl.getDouble());
highBits[i] = (int) (l >> 32);
loBits[i] = (int) l;
i++;
}
byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Double_hi[" + highBits.length + "]");
encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Double_lo[" + loBits.length + "]");
}
private void writeCpString(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_String.size() + " String entries...");
int[] cpString = new int[cp_String.size()];
int i = 0;
for (Iterator iterator = cp_String.iterator(); iterator.hasNext();) {
CPString cpStr = (CPString) iterator.next();
cpString[i] = cpStr.getIndexInCpUtf8();
i++;
}
byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpString[" + cpString.length + "]");
}
private void writeCpClass(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
int[] cpClass = new int[cp_Class.size()];
int i = 0;
for (Iterator iterator = cp_Class.iterator(); iterator.hasNext();) {
CPClass cpCl = (CPClass) iterator.next();
cpClass[i] = cpCl.getIndexInCpUtf8();
i++;
}
byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpClass[" + cpClass.length + "]");
}
private void writeCpSignature(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
int[] cpSignatureForm = new int[cp_Signature.size()];
List classes = new ArrayList();
int i = 0;
for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
CPSignature cpS = (CPSignature) iterator.next();
classes.addAll(cpS.getClasses());
cpSignatureForm[i] = cpS.getIndexInCpUtf8();
i++;
}
int[] cpSignatureClasses = new int[classes.size()];
for (int j = 0; j < cpSignatureClasses.length; j++) {
cpSignatureClasses[j] = ((CPClass) classes.get(j)).getIndex();
}
byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm,
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses,
Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
}
private void writeCpDescr(OutputStream out) throws IOException,
Pack200Exception {
PackingUtils.log("Writing " + cp_Descr.size()
+ " Descriptor entries...");
int[] cpDescrName = new int[cp_Descr.size()];
int[] cpDescrType = new int[cp_Descr.size()];
int i = 0;
for (Iterator iterator = cp_Descr.iterator(); iterator.hasNext();) {
CPNameAndType nameAndType = (CPNameAndType) iterator.next();
cpDescrName[i] = nameAndType.getNameIndex();
cpDescrType[i] = nameAndType.getTypeIndex();
i++;
}
byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName,
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
}
private void writeCpMethodOrField(Set cp, OutputStream out, String name)
throws IOException, Pack200Exception {
PackingUtils.log("Writing " + cp.size()
+ " Method and Field entries...");
int[] cp_methodOrField_class = new int[cp.size()];
int[] cp_methodOrField_desc = new int[cp.size()];
int i = 0;
for (Iterator iterator = cp.iterator(); iterator.hasNext();) {
CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
cp_methodOrField_class[i] = mOrF.getClassIndex();
cp_methodOrField_desc[i] = mOrF.getDescIndex();
i++;
}
byte[] encodedBand = encodeBandInt(name + "_class",
cp_methodOrField_class, Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ name + "_class[" + cp_methodOrField_class.length + "]");
encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc,
Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ name + "_desc[" + cp_methodOrField_desc.length + "]");
}
/**
* All input classes for the segment have now been read in, so this method
* is called so that this class can calculate/complete anything it could not
* do while classes were being read.
*/
public void finaliseBands() {
addCPUtf8("");
removeSignaturesFromCpUTF8();
addIndices();
segmentHeader.setCp_Utf8_count(cp_Utf8.size());
segmentHeader.setCp_Int_count(cp_Int.size());
segmentHeader.setCp_Float_count(cp_Float.size());
segmentHeader.setCp_Long_count(cp_Long.size());
segmentHeader.setCp_Double_count(cp_Double.size());
segmentHeader.setCp_String_count(cp_String.size());
segmentHeader.setCp_Class_count(cp_Class.size());
segmentHeader.setCp_Signature_count(cp_Signature.size());
segmentHeader.setCp_Descr_count(cp_Descr.size());
segmentHeader.setCp_Field_count(cp_Field.size());
segmentHeader.setCp_Method_count(cp_Method.size());
segmentHeader.setCp_Imethod_count(cp_Imethod.size());
}
private void removeSignaturesFromCpUTF8() {
for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
CPSignature signature = (CPSignature) iterator.next();
String sigStr = signature.getUnderlyingString();
CPUTF8 utf8 = signature.getSignatureForm();
String form = utf8.getUnderlyingString();
if (!sigStr.equals(form)) {
removeCpUtf8(sigStr);
}
}
}
private void addIndices() {
Set[] sets = new Set[] { cp_Utf8, cp_Int, cp_Float, cp_Long, cp_Double,
cp_String, cp_Class, cp_Signature, cp_Descr, cp_Field,
cp_Method, cp_Imethod };
for (int i = 0; i < sets.length; i++) {
int j = 0;
for (Iterator iterator = sets[i].iterator(); iterator.hasNext();) {
ConstantPoolEntry entry = (ConstantPoolEntry) iterator.next();
entry.setIndex(j);
j++;
}
}
Map classNameToIndex = new HashMap();
for (Iterator iterator = cp_Field.iterator(); iterator.hasNext();) {
CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
CPClass className = mOrF.getClassName();
Integer index = (Integer) classNameToIndex.get(className);
if (index == null) {
classNameToIndex.put(className, new Integer(1));
mOrF.setIndexInClass(0);
} else {
int theIndex = index.intValue();
mOrF.setIndexInClass(theIndex);
classNameToIndex.put(className, new Integer(theIndex + 1));
}
}
classNameToIndex.clear();
Map classNameToConstructorIndex = new HashMap();
for (Iterator iterator = cp_Method.iterator(); iterator.hasNext();) {
CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
CPClass className = mOrF.getClassName();
Integer index = (Integer) classNameToIndex.get(className);
if (index == null) {
classNameToIndex.put(className, new Integer(1));
mOrF.setIndexInClass(0);
} else {
int theIndex = index.intValue();
mOrF.setIndexInClass(theIndex);
classNameToIndex.put(className, new Integer(theIndex + 1));
}
if(mOrF.getDesc().getName().equals("<init>")) {
Integer constructorIndex = (Integer) classNameToConstructorIndex.get(className);
if (constructorIndex == null) {
classNameToConstructorIndex.put(className, new Integer(1));
mOrF.setIndexInClassForConstructor(0);
} else {
int theIndex = constructorIndex.intValue();
mOrF.setIndexInClassForConstructor(theIndex);
classNameToConstructorIndex.put(className, new Integer(theIndex + 1));
}
}
}
}
private void removeCpUtf8(String string) {
CPUTF8 utf8 = (CPUTF8) stringsToCpUtf8.get(string);
if (utf8 != null) {
if(stringsToCpClass.get(string) == null) { // don't remove if strings are also in cpclass
stringsToCpUtf8.remove(string);
cp_Utf8.remove(utf8);
}
}
}
void addCPUtf8(String utf8) {
getCPUtf8(utf8);
}
public CPUTF8 getCPUtf8(String utf8) {
if(utf8 == null) {
return null;
}
CPUTF8 cpUtf8 = (CPUTF8) stringsToCpUtf8.get(utf8);
if (cpUtf8 == null) {
cpUtf8 = new CPUTF8(utf8);
cp_Utf8.add(cpUtf8);
stringsToCpUtf8.put(utf8, cpUtf8);
}
return cpUtf8;
}
public void addCPNameAndType(String name, String signature) {
getCPNameAndType(name, signature);
}
public void addCPSignature(String signature) {
getCPSignature(signature);
}
public CPSignature getCPSignature(String signature) {
if(signature == null) {
return null;
}
CPSignature cpS = (CPSignature) stringsToCpSignature.get(signature);
if (cpS == null) {
List cpClasses = new ArrayList();
CPUTF8 signatureUTF8;
if (signature.length() > 1 && signature.indexOf('L') != -1) {
List classes = new ArrayList();
char[] chars = signature.toCharArray();
StringBuffer signatureString = new StringBuffer();
for (int i = 0; i < chars.length; i++) {
signatureString.append(chars[i]);
if (chars[i] == 'L') {
StringBuffer className = new StringBuffer();
for (int j = i + 1; j < chars.length; j++) {
char c = chars[j];
if (Character.isLetter(c) || Character.isDigit(c)
|| c == '/' || c == '$' || c == '_') {
className.append(c);
} else {
classes.add(className.toString());
i = j - 1;
break;
}
}
}
}
removeCpUtf8(signature);
for (Iterator iterator2 = classes.iterator(); iterator2
.hasNext();) {
String className = (String) iterator2.next();
CPClass cpClass = null;
if(className!= null) {
className = className.replace('.', '/');
cpClass = (CPClass) stringsToCpClass.get(className);
if (cpClass == null) {
CPUTF8 cpUtf8 = getCPUtf8(className);
cpClass = new CPClass(cpUtf8);
cp_Class.add(cpClass);
stringsToCpClass.put(className, cpClass);
}
}
cpClasses.add(cpClass);
}
signatureUTF8 = getCPUtf8(signatureString.toString());
} else {
signatureUTF8 = getCPUtf8(signature);
}
cpS = new CPSignature(signature, signatureUTF8, cpClasses);
cp_Signature.add(cpS);
stringsToCpSignature.put(signature, cpS);
}
return cpS;
}
public CPClass getCPClass(String className) {
if(className == null) {
return null;
}
className = className.replace('.', '/');
CPClass cpClass = (CPClass) stringsToCpClass.get(className);
if (cpClass == null) {
CPUTF8 cpUtf8 = getCPUtf8(className);
cpClass = new CPClass(cpUtf8);
cp_Class.add(cpClass);
stringsToCpClass.put(className, cpClass);
}
if(cpClass.isInnerClass()) {
segment.getClassBands().currentClassReferencesInnerClass(cpClass);
}
return cpClass;
}
public void addCPClass(String className) {
getCPClass(className);
}
public CPNameAndType getCPNameAndType(String name, String signature) {
String descr = name + ":" + signature;
CPNameAndType nameAndType = (CPNameAndType) stringsToCpNameAndType
.get(descr);
if (nameAndType == null) {
nameAndType = new CPNameAndType(getCPUtf8(name),
getCPSignature(signature));
stringsToCpNameAndType.put(descr, nameAndType);
cp_Descr.add(nameAndType);
}
return nameAndType;
}
public CPMethodOrField getCPField(CPClass cpClass, String name, String desc) {
String key = cpClass.toString() + ":" + name + ":" + desc;
CPMethodOrField cpF = (CPMethodOrField) stringsToCpField
.get(key);
if (cpF == null) {
CPNameAndType nAndT = getCPNameAndType(name, desc);
cpF = new CPMethodOrField(cpClass, nAndT);
cp_Field.add(cpF);
stringsToCpField.put(key, cpF);
}
return cpF;
}
public CPConstant getConstant(Object value) {
CPConstant constant = (CPConstant) objectsToCPConstant.get(value);
if (constant == null) {
if (value instanceof Integer) {
constant = new CPInt(((Integer) value).intValue());
cp_Int.add(constant);
} else if (value instanceof Long) {
constant = new CPLong(((Long) value).longValue());
cp_Long.add(constant);
} else if (value instanceof Float) {
constant = new CPFloat(((Float) value).floatValue());
cp_Float.add(constant);
} else if (value instanceof Double) {
constant = new CPDouble(((Double) value).doubleValue());
cp_Double.add(constant);
} else if (value instanceof String) {
constant = new CPString(getCPUtf8((String) value));
cp_String.add(constant);
} else if (value instanceof Type) {
String className = ((Type) value).getClassName();
if(className.endsWith("[]")) {
className = "[L" + className.substring(0, className.length() - 2);
while(className.endsWith("[]")) {
className = "[" + className.substring(0, className.length() - 2);
}
className += ";";
}
constant = getCPClass(className);
}
objectsToCPConstant.put(value, constant);
}
return constant;
}
public CPMethodOrField getCPMethod(CPClass cpClass, String name, String desc) {
String key = cpClass.toString() + ":" + name + ":" + desc;
CPMethodOrField cpM = (CPMethodOrField) stringsToCpMethod
.get(key);
if (cpM == null) {
CPNameAndType nAndT = getCPNameAndType(name, desc);
cpM = new CPMethodOrField(cpClass, nAndT);
cp_Method.add(cpM);
stringsToCpMethod.put(key, cpM);
}
return cpM;
}
public CPMethodOrField getCPIMethod(CPClass cpClass, String name,
String desc) {
String key = cpClass.toString() + ":" + name + ":" + desc;
CPMethodOrField cpIM = (CPMethodOrField) stringsToCpIMethod
.get(key);
if (cpIM == null) {
CPNameAndType nAndT = getCPNameAndType(name, desc);
cpIM = new CPMethodOrField(cpClass, nAndT);
cp_Imethod.add(cpIM);
stringsToCpIMethod.put(key, cpIM);
}
return cpIM;
}
public CPMethodOrField getCPField(String owner, String name, String desc) {
return getCPField(getCPClass(owner), name, desc);
}
public CPMethodOrField getCPMethod(String owner, String name, String desc) {
return getCPMethod(getCPClass(owner), name, desc);
}
public CPMethodOrField getCPIMethod(String owner, String name, String desc) {
return getCPIMethod(getCPClass(owner), name, desc);
}
public boolean existsCpClass(String className) {
CPClass cpClass = (CPClass) stringsToCpClass.get(className);
return cpClass != null;
}
}

@ -0,0 +1,170 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import org.apache.harmony.pack200.Archive.PackingFile;
import org.apache.harmony.pack200.Archive.SegmentUnit;
import org.objectweb.asm.ClassReader;
/**
* Bands containing information about files in the pack200 archive and the file
* contents for non-class-files. Corresponds to the <code>file_bands</code> set
* of bands described in the specification.
*/
public class FileBands extends BandSet {
private final CPUTF8[] fileName;
private int[] file_name;
private final int[] file_modtime;
private final long[] file_size;
private final int[] file_options;
private final byte[][] file_bits;
public FileBands(CpBands cpBands, SegmentHeader segmentHeader,
PackingOptions options, SegmentUnit segmentUnit, int effort) {
super(effort, segmentHeader);
List fileList = segmentUnit.getFileList();
int size = fileList.size();
fileName = new CPUTF8[size];
file_modtime = new int[size];
file_size = new long[size];
file_options = new int[size];
int totalSize = 0;
file_bits = new byte[size][];
int archiveModtime = segmentHeader.getArchive_modtime();
Set classNames = new HashSet();
for (Iterator iterator = segmentUnit.getClassList().iterator(); iterator
.hasNext();) {
ClassReader reader = (ClassReader) iterator.next();
classNames.add(reader.getClassName());
}
CPUTF8 emptyString = cpBands.getCPUtf8("");
long modtime;
int latestModtime = Integer.MIN_VALUE;
boolean isLatest = !PackingOptions.KEEP.equals(options
.getModificationTime());
for (int i = 0; i < size; i++) {
PackingFile packingFile = (PackingFile) fileList.get(i);
String name = packingFile.getName();
if (name.endsWith(".class") && !options.isPassFile(name)) {
file_options[i] |= (1 << 1);
if (classNames.contains(name.substring(0, name.length() - 6))) {
fileName[i] = emptyString;
} else {
fileName[i] = cpBands.getCPUtf8(name);
}
} else {
fileName[i] = cpBands.getCPUtf8(name);
}
// set deflate_hint for file element
if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
file_options[i] |= 0x1;
}
byte[] bytes = packingFile.getContents();
file_size[i] = bytes.length;
totalSize += file_size[i];
// update modification time
modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
file_modtime[i] = (int) (modtime - archiveModtime);
if (isLatest && latestModtime < file_modtime[i]) {
latestModtime = file_modtime[i];
}
file_bits[i] = packingFile.getContents();
}
if (isLatest) {
for (int index = 0; index < size; index++) {
file_modtime[index] = latestModtime;
}
}
}
/**
* All input classes for the segment have now been read in, so this method
* is called so that this class can calculate/complete anything it could not
* do while classes were being read.
*/
public void finaliseBands() {
file_name = new int[fileName.length];
for (int i = 0; i < file_name.length; i++) {
file_name[i] = fileName[i].getIndex();
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing file bands...");
byte[] encodedBand = encodeBandInt("file_name", file_name,
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from file_name[" + file_name.length + "]");
encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5,
Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from file_size[" + file_size.length + "]");
if (segmentHeader.have_file_modtime()) {
encodedBand = encodeBandInt("file_modtime", file_modtime,
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from file_modtime[" + file_modtime.length + "]");
}
if (segmentHeader.have_file_options()) {
encodedBand = encodeBandInt("file_options", file_options,
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from file_options[" + file_options.length + "]");
}
encodedBand = encodeBandInt("file_bits", flatten(file_bits),
Codec.BYTE1);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from file_bits[" + file_bits.length + "]");
}
private int[] flatten(byte[][] bytes) {
int total = 0;
for (int i = 0; i < bytes.length; i++) {
total += bytes[i].length;
}
int[] band = new int[total];
int index = 0;
for (int i = 0; i < bytes.length; i++) {
for (int j = 0; j < bytes[i].length; j++) {
band[index++] = bytes[i][j] & 0xFF;
}
}
return band;
}
}

@ -0,0 +1,203 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Inner class bands (corresponds to the <code>ic_bands</code> set of bands in
* the pack200 specification)
*/
public class IcBands extends BandSet {
private final Set innerClasses = new TreeSet();
private final CpBands cpBands;
private int bit16Count = 0;
private final Map outerToInner = new HashMap();
public IcBands(SegmentHeader segmentHeader, CpBands cpBands, int effort) {
super(effort, segmentHeader);
this.cpBands = cpBands;
}
/**
* All input classes for the segment have now been read in, so this method
* is called so that this class can calculate/complete anything it could not
* do while classes were being read.
*/
public void finaliseBands() {
segmentHeader.setIc_count(innerClasses.size());
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing internal class bands...");
int[] ic_this_class = new int[innerClasses.size()];
int[] ic_flags = new int[innerClasses.size()];
int[] ic_outer_class = new int[bit16Count];
int[] ic_name = new int[bit16Count];
int index2 = 0;
List innerClassesList = new ArrayList(innerClasses);
for (int i = 0; i < ic_this_class.length; i++) {
IcTuple icTuple = (IcTuple) innerClassesList.get(i);
ic_this_class[i] = icTuple.C.getIndex();
ic_flags[i] = icTuple.F;
if((icTuple.F & (1<<16)) != 0) {
ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
index2++;
}
}
byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class,
Codec.UDELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from ic_this_class[" + ic_this_class.length + "]");
encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from ic_flags[" + ic_flags.length + "]");
encodedBand = encodeBandInt("ic_outer_class", ic_outer_class,
Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from ic_outer_class[" + ic_outer_class.length + "]");
encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from ic_name[" + ic_name.length + "]");
}
public void addInnerClass(String name, String outerName, String innerName,
int flags) {
if(outerName != null || innerName != null) {
if(namesArePredictable(name, outerName, innerName)) {
IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
addToMap(outerName, innerClass);
innerClasses.add(innerClass);
} else {
flags |= (1<<16);
IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
boolean added = innerClasses.add(icTuple);
if(added) {
bit16Count++;
addToMap(outerName, icTuple);
}
}
} else {
IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
addToMap(getOuter(name), innerClass);
innerClasses.add(innerClass);
}
}
public List getInnerClassesForOuter(String outerClassName) {
return (List) outerToInner.get(outerClassName);
}
private String getOuter(String name) {
return name.substring(0, name.lastIndexOf('$'));
}
private void addToMap(String outerName, IcTuple icTuple) {
List tuples = (List) outerToInner.get(outerName);
if(tuples == null) {
tuples = new ArrayList();
outerToInner.put(outerName, tuples);
tuples.add(icTuple);
} else {
for (Iterator iterator = tuples.iterator(); iterator.hasNext();) {
IcTuple icT = (IcTuple) iterator.next();
if(icTuple.equals(icT)) {
return;
}
}
tuples.add(icTuple);
}
}
private boolean namesArePredictable(String name, String outerName,
String innerName) {
// TODO: Could be multiple characters, not just $
return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
}
class IcTuple implements Comparable {
protected CPClass C; // this class
protected int F; // flags
protected CPClass C2; // outer class
protected CPUTF8 N; // name
public IcTuple(CPClass C, int F, CPClass C2, CPUTF8 N) {
this.C = C;
this.F = F;
this.C2 = C2;
this.N = N;
}
public boolean equals(Object o) {
if(o instanceof IcTuple) {
IcTuple icT = (IcTuple)o;
return C.equals(icT.C) && F == icT.F && (C2 != null ? C2.equals(icT.C2) : icT.C2 == null) && (N != null ? N.equals(icT.N) : icT.N == null);
}
return false;
}
public int hashCode() {
return (C.hashCode() * 37) + F;
}
public String toString() {
return C.toString();
}
public int compareTo(Object arg0) {
return C.compareTo(((IcTuple)arg0).C);
}
public boolean isAnonymous() {
String className = C.toString();
String innerName = className.substring(className.lastIndexOf('$') + 1);
return Character.isDigit(innerName.charAt(0));
}
}
public IcTuple getIcTuple(CPClass inner) {
for (Iterator iterator = innerClasses.iterator(); iterator.hasNext();) {
IcTuple icTuple = (IcTuple) iterator.next();
if(icTuple.C.equals(inner)) {
return icTuple;
}
}
return null;
}
}

@ -0,0 +1,253 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.util.Arrays;
/**
* IntList is based on <code>java.util.ArrayList</code>, but is written
* specifically for ints in order to reduce boxing and unboxing to Integers,
* reduce the memory required and improve performance of pack200.
*/
public class IntList {
private int[] array;
private int firstIndex;
private int lastIndex;
private int modCount;
/**
* Constructs a new instance of IntList with capacity for ten elements.
*/
public IntList() {
this(10);
}
/**
* Constructs a new instance of IntList with the specified capacity.
*
* @param capacity
* the initial capacity of this IntList
*/
public IntList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException();
}
firstIndex = lastIndex = 0;
array = new int[capacity];
}
/**
* Adds the specified object at the end of this IntList.
*
* @param object
* the object to add
* @return true
*/
public boolean add(int object) {
if (lastIndex == array.length) {
growAtEnd(1);
}
array[lastIndex++] = object;
modCount++;
return true;
}
public void add(int location, int object) {
int size = lastIndex - firstIndex;
if (0 < location && location < size) {
if (firstIndex == 0 && lastIndex == array.length) {
growForInsert(location, 1);
} else if ((location < size / 2 && firstIndex > 0)
|| lastIndex == array.length) {
System.arraycopy(array, firstIndex, array, --firstIndex,
location);
} else {
int index = location + firstIndex;
System.arraycopy(array, index, array, index + 1, size
- location);
lastIndex++;
}
array[location + firstIndex] = object;
} else if (location == 0) {
if (firstIndex == 0) {
growAtFront(1);
}
array[--firstIndex] = object;
} else if (location == size) {
if (lastIndex == array.length) {
growAtEnd(1);
}
array[lastIndex++] = object;
} else {
throw new IndexOutOfBoundsException();
}
modCount++;
}
public void clear() {
if (firstIndex != lastIndex) {
Arrays.fill(array, firstIndex, lastIndex, -1);
firstIndex = lastIndex = 0;
modCount++;
}
}
public int get(int location) {
if (0 <= location && location < (lastIndex - firstIndex)) {
return array[firstIndex + location];
}
throw new IndexOutOfBoundsException("" + location);
}
private void growAtEnd(int required) {
int size = lastIndex - firstIndex;
if (firstIndex >= required - (array.length - lastIndex)) {
int newLast = lastIndex - firstIndex;
if (size > 0) {
System.arraycopy(array, firstIndex, array, 0, size);
}
firstIndex = 0;
lastIndex = newLast;
} else {
int increment = size / 2;
if (required > increment) {
increment = required;
}
if (increment < 12) {
increment = 12;
}
int[] newArray = new int[size + increment];
if (size > 0) {
System.arraycopy(array, firstIndex, newArray, 0, size);
firstIndex = 0;
lastIndex = size;
}
array = newArray;
}
}
private void growAtFront(int required) {
int size = lastIndex - firstIndex;
if (array.length - lastIndex + firstIndex >= required) {
int newFirst = array.length - size;
if (size > 0) {
System.arraycopy(array, firstIndex, array, newFirst, size);
}
firstIndex = newFirst;
lastIndex = array.length;
} else {
int increment = size / 2;
if (required > increment) {
increment = required;
}
if (increment < 12) {
increment = 12;
}
int[] newArray = new int[size + increment];
if (size > 0) {
System.arraycopy(array, firstIndex, newArray, newArray.length
- size, size);
}
firstIndex = newArray.length - size;
lastIndex = newArray.length;
array = newArray;
}
}
private void growForInsert(int location, int required) {
int size = lastIndex - firstIndex;
int increment = size / 2;
if (required > increment) {
increment = required;
}
if (increment < 12) {
increment = 12;
}
int[] newArray = new int[size + increment];
int newFirst = increment - required;
// Copy elements after location to the new array skipping inserted
// elements
System.arraycopy(array, location + firstIndex, newArray, newFirst
+ location + required, size - location);
// Copy elements before location to the new array from firstIndex
System.arraycopy(array, firstIndex, newArray, newFirst, location);
firstIndex = newFirst;
lastIndex = size + increment;
array = newArray;
}
public void increment(int location) {
if (0 <= location && location < (lastIndex - firstIndex)) {
array[firstIndex + location]++;
} else {
throw new IndexOutOfBoundsException("" + location);
}
}
public boolean isEmpty() {
return lastIndex == firstIndex;
}
public int remove(int location) {
int result;
int size = lastIndex - firstIndex;
if (0 <= location && location < size) {
if (location == size - 1) {
result = array[--lastIndex];
array[lastIndex] = 0;
} else if (location == 0) {
result = array[firstIndex];
array[firstIndex++] = 0;
} else {
int elementIndex = firstIndex + location;
result = array[elementIndex];
if (location < size / 2) {
System.arraycopy(array, firstIndex, array, firstIndex + 1,
location);
array[firstIndex++] = 0;
} else {
System.arraycopy(array, elementIndex + 1, array,
elementIndex, size - location - 1);
array[--lastIndex] = 0;
}
}
if (firstIndex == lastIndex) {
firstIndex = lastIndex = 0;
}
} else {
throw new IndexOutOfBoundsException();
}
modCount++;
return result;
}
public int size() {
return lastIndex - firstIndex;
}
public int[] toArray() {
int size = lastIndex - firstIndex;
int[] result = new int[size];
System.arraycopy(array, firstIndex, result, 0, size);
return result;
}
}

@ -0,0 +1,375 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A group of metadata (annotation) bands, such as class_RVA_bands,
* method_AD_bands etc.
*/
public class MetadataBandGroup extends BandSet {
public static final int CONTEXT_CLASS = 0;
public static final int CONTEXT_FIELD = 1;
public static final int CONTEXT_METHOD = 2;
private final String type;
private int numBackwardsCalls = 0;
public List param_NB = new ArrayList(); // TODO: Lazy instantiation?
public IntList anno_N = new IntList();
public List type_RS = new ArrayList();
public IntList pair_N = new IntList();
public List name_RU = new ArrayList();
public List T = new ArrayList();
public List caseI_KI = new ArrayList();
public List caseD_KD = new ArrayList();
public List caseF_KF = new ArrayList();
public List caseJ_KJ = new ArrayList();
public List casec_RS = new ArrayList();
public List caseet_RS = new ArrayList();
public List caseec_RU = new ArrayList();
public List cases_RU = new ArrayList();
public IntList casearray_N = new IntList();
public List nesttype_RS = new ArrayList();
public IntList nestpair_N = new IntList();
public List nestname_RU = new ArrayList();
private final CpBands cpBands;
private final int context;
/**
* Constructs a new MetadataBandGroup
*
* @param type
* must be either AD, RVA, RIA, RVPA or RIPA.
* @param context
* <code>CONTEXT_CLASS</code>, <code>CONTEXT_METHOD</code> or
* <code>CONTEXT_FIELD</code>
* @param cpBands
* constant pool bands
* @param segmentHeader
* segment header
* @param effort
* packing effort
*/
public MetadataBandGroup(String type, int context, CpBands cpBands, SegmentHeader segmentHeader, int effort) {
super(effort, segmentHeader);
this.type = type;
this.cpBands = cpBands;
this.context = context;
}
/* (non-Javadoc)
* @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream)
*/
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing metadata band group...");
if(hasContent()) {
String contextStr;
if(context == CONTEXT_CLASS) {
contextStr = "Class";
} else if (context == CONTEXT_FIELD) {
contextStr = "Field";
} else {
contextStr = "Method";
}
byte[] encodedBand = null;
if(!type.equals("AD")) {
encodedBand = encodeBandInt(
contextStr + "_" + type + " anno_N", anno_N.toArray(),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from " + contextStr + "_" + type + " anno_N["
+ anno_N.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type
+ " type_RS", cpEntryListToArray(type_RS),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from " + contextStr + "_" + type
+ " type_RS[" + type_RS.size() + "]");
encodedBand = encodeBandInt(
contextStr + "_" + type + " pair_N", pair_N.toArray(),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from " + contextStr + "_" + type + " pair_N["
+ pair_N.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type
+ " name_RU", cpEntryListToArray(name_RU),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length
+ " bytes from " + contextStr + "_" + type
+ " name_RU[" + name_RU.size() + "]");
}
encodedBand = encodeBandInt(contextStr + "_" + type + " T",
tagListToArray(T), Codec.BYTE1);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " T[" + T.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI",
cpEntryListToArray(caseI_KI), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseI_KI[" + caseI_KI.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD",
cpEntryListToArray(caseD_KD), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseD_KD[" + caseD_KD.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF",
cpEntryListToArray(caseF_KF), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseF_KF[" + caseF_KF.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ",
cpEntryListToArray(caseJ_KJ), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseJ_KJ[" + caseJ_KJ.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS",
cpEntryListToArray(casec_RS), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " casec_RS[" + casec_RS.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS",
cpEntryListToArray(caseet_RS), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseet_RS["
+ caseet_RS.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU",
cpEntryListToArray(caseec_RU), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " caseec_RU["
+ caseec_RU.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU",
cpEntryListToArray(cases_RU), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " cases_RU[" + cases_RU.size()
+ "]");
encodedBand = encodeBandInt(contextStr + "_" + type
+ " casearray_N", casearray_N.toArray(), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " casearray_N["
+ casearray_N.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type
+ " nesttype_RS", cpEntryListToArray(nesttype_RS),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " nesttype_RS["
+ nesttype_RS.size() + "]");
encodedBand = encodeBandInt(
contextStr + "_" + type + " nestpair_N", nestpair_N
.toArray(), Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " nestpair_N["
+ nestpair_N.size() + "]");
encodedBand = encodeBandInt(contextStr + "_" + type
+ " nestname_RU", cpEntryListToArray(nestname_RU),
Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ contextStr + "_" + type + " nestname_RU["
+ nestname_RU.size() + "]");
}
}
private int[] tagListToArray(List t2) {
int[] ints = new int[t2.size()];
for (int i = 0; i < ints.length; i++) {
ints[i] = ((String)t2.get(i)).charAt(0);
}
return ints;
}
/**
* Add an annotation to this set of bands
*
* @param desc
* @param nameRU
* @param t
* @param values
* @param caseArrayN
* @param nestTypeRS
* @param nestNameRU
* @param nestPairN
*/
public void addAnnotation(String desc, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
type_RS.add(cpBands.getCPSignature(desc));
pair_N.add(t.size());
for (Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
String name = (String) iterator.next();
name_RU.add(cpBands.getCPUtf8(name));
}
Iterator valuesIterator = values.iterator();
for (Iterator iterator = t.iterator(); iterator.hasNext();) {
String tag = (String) iterator.next();
T.add(tag);
if (tag.equals("B") || tag.equals("C") || tag.equals("I")
|| tag.equals("S") || tag.equals("Z")) {
Integer value = (Integer)valuesIterator.next();
caseI_KI.add(cpBands.getConstant(value));
} else if (tag.equals("D")) {
Double value = (Double)valuesIterator.next();
caseD_KD.add(cpBands.getConstant(value));
} else if (tag.equals("F")) {
Float value = (Float)valuesIterator.next();
caseF_KF.add(cpBands.getConstant(value));
} else if (tag.equals("J")) {
Long value = (Long)valuesIterator.next();
caseJ_KJ.add(cpBands.getConstant(value));
} else if (tag.equals("C")) {
String value = (String)valuesIterator.next();
casec_RS.add(cpBands.getCPSignature(value));
} else if (tag.equals("e")) {
String value = (String)valuesIterator.next();
String value2 = (String)valuesIterator.next();
caseet_RS.add(cpBands.getCPSignature(value));
caseec_RU.add(cpBands.getCPUtf8(value2));
} else if (tag.equals("s")) {
String value = (String)valuesIterator.next();
cases_RU.add(cpBands.getCPUtf8(value));
}
// do nothing here for [ or @ (handled below)
}
for (Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
int arraySize = ((Integer)iterator.next()).intValue();
casearray_N.add(arraySize);
numBackwardsCalls += arraySize;
}
for (Iterator iterator = nesttype_RS.iterator(); iterator.hasNext();) {
String type = (String) iterator.next();
nesttype_RS.add(cpBands.getCPSignature(type));
}
for (Iterator iterator = nestname_RU.iterator(); iterator.hasNext();) {
String name = (String) iterator.next();
nestname_RU.add(cpBands.getCPUtf8(name));
}
for (Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
Integer numPairs = (Integer) iterator.next();
nestPairN.add(numPairs);
numBackwardsCalls += numPairs.intValue();
}
}
/**
* Returns true if any annotations have been added to this set of bands
*/
public boolean hasContent() {
return type_RS.size() > 0;
}
public int numBackwardsCalls() {
return numBackwardsCalls;
}
public void incrementAnnoN() {
anno_N.increment(anno_N.size() - 1);
}
public void newEntryInAnnoN() {
anno_N.add(1);
}
/**
* Remove the latest annotation that was added to this group
*/
public void removeLatest() {
int latest = anno_N.remove(anno_N.size() -1);
for (int i = 0; i < latest; i++) {
type_RS.remove(type_RS.size() - 1);
int pairs = pair_N.remove(pair_N.size() - 1);
for (int j = 0; j < pairs; j++) {
removeOnePair();
}
}
}
/*
* Convenience method for removeLatest
*/
private void removeOnePair() {
String tag = (String) T.remove(T.size() - 1);
if (tag.equals("B") || tag.equals("C") || tag.equals("I")
|| tag.equals("S") || tag.equals("Z")) {
caseI_KI.remove(caseI_KI.size() - 1);
} else if (tag.equals("D")) {
caseD_KD.remove(caseD_KD.size() - 1);
} else if (tag.equals("F")) {
caseF_KF.remove(caseF_KF.size() - 1);
} else if (tag.equals("J")) {
caseJ_KJ.remove(caseJ_KJ.size() - 1);
} else if (tag.equals("C")) {
casec_RS.remove(casec_RS.size() - 1);
} else if (tag.equals("e")) {
caseet_RS.remove(caseet_RS.size() - 1);
caseec_RU.remove(caseet_RS.size() - 1);
} else if (tag.equals("s")) {
cases_RU.remove(cases_RU.size() - 1);
} else if (tag.equals("[")) {
int arraySize = casearray_N.remove(casearray_N.size() - 1);
numBackwardsCalls -= arraySize;
for (int k = 0; k < arraySize; k++) {
removeOnePair();
}
} else if (tag.equals("@")) {
nesttype_RS.remove(nesttype_RS.size() - 1);
int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
numBackwardsCalls -= numPairs;
for (int i = 0; i < numPairs; i++) {
removeOnePair();
}
}
}
}

@ -0,0 +1,145 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Label;
/**
* NewAttribute extends <code>Attribute</code> and manages unknown attributes
* encountered by ASM that have had a layout definition given to pack200 (e.g.
* via one of the -C, -M, -F or -D command line options)
*/
public class NewAttribute extends Attribute {
private boolean contextClass = false;
private boolean contextMethod = false;
private boolean contextField = false;
private boolean contextCode = false;
private final String layout;
private byte[] contents;
private int codeOff;
private Label[] labels;
private ClassReader classReader;
private char[] buf;
public NewAttribute(String type, String layout, int context) {
super(type);
this.layout = layout;
addContext(context);
}
public NewAttribute(ClassReader classReader, String type, String layout, byte[] contents, char[] buf,
int codeOff, Label[] labels) {
super(type);
this.classReader = classReader;
this.contents = contents;
this.layout = layout;
this.codeOff = codeOff;
this.labels = labels;
this.buf = buf;
}
public void addContext(int context) {
switch(context) {
case AttributeDefinitionBands.CONTEXT_CLASS:
contextClass = true;
break;
case AttributeDefinitionBands.CONTEXT_METHOD:
contextMethod = true;
break;
case AttributeDefinitionBands.CONTEXT_FIELD:
contextField = true;
break;
case AttributeDefinitionBands.CONTEXT_CODE:
contextCode = true;
break;
}
}
public boolean isContextClass() {
return contextClass;
}
public boolean isContextMethod() {
return contextMethod;
}
public boolean isContextField() {
return contextField;
}
public boolean isContextCode() {
return contextCode;
}
public String getLayout() {
return layout;
}
public boolean isUnknown() {
return false;
}
public boolean isCodeAttribute() {
return codeOff != -1;
}
protected Attribute read(ClassReader cr, int off, int len, char[] buf,
int codeOff, Label[] labels) {
byte[] attributeContents = new byte[len];
System.arraycopy(cr.b, off, attributeContents, 0, len);
return new NewAttribute(cr, type, layout, attributeContents, buf, codeOff,
labels);
}
public boolean isUnknown(int context) {
switch(context) {
case AttributeDefinitionBands.CONTEXT_CLASS:
return contextClass;
case AttributeDefinitionBands.CONTEXT_METHOD:
return contextMethod;
case AttributeDefinitionBands.CONTEXT_FIELD:
return contextField;
case AttributeDefinitionBands.CONTEXT_CODE:
return contextCode;
}
return false;
}
public String readUTF8(int index) {
return classReader.readUTF8(index, buf);
}
public String readClass(int index) {
return classReader.readClass(index, buf);
}
public Object readConst(int index) {
return classReader.readConst(index, buf);
}
public byte[] getBytes() {
return contents;
}
public Label getLabel(int index) {
return labels[index];
}
}

@ -0,0 +1,968 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
import org.objectweb.asm.Label;
/**
* Set of bands relating to a non-predefined attribute that has had a layout
* definition given to pack200 (e.g. via one of the -C, -M, -F or -D command
* line options)
*/
public class NewAttributeBands extends BandSet {
protected List attributeLayoutElements;
private int[] backwardsCallCounts;
private final CpBands cpBands;
private final AttributeDefinition def;
private boolean usedAtLeastOnce;
// used when parsing
private Integral lastPIntegral;
public NewAttributeBands(int effort, CpBands cpBands, SegmentHeader header, AttributeDefinition def) throws IOException {
super(effort, header);
this.def = def;
this.cpBands = cpBands;
parseLayout();
}
public void addAttribute(NewAttribute attribute) {
usedAtLeastOnce = true;
InputStream stream = new ByteArrayInputStream(attribute.getBytes());
for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.addAttributeToBand(attribute, stream);
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.pack(out);
}
}
public String getAttributeName() {
return def.name.getUnderlyingString();
}
public int getFlagIndex() {
return def.index;
}
public int[] numBackwardsCalls() {
return backwardsCallCounts;
}
public boolean isUsedAtLeastOnce() {
return usedAtLeastOnce;
}
private void parseLayout() throws IOException {
String layout = def.layout.getUnderlyingString();
if (attributeLayoutElements == null) {
attributeLayoutElements = new ArrayList();
StringReader stream = new StringReader(layout);
AttributeLayoutElement e;
while ((e = readNextAttributeElement(stream)) != null) {
attributeLayoutElements.add(e);
}
resolveCalls();
}
}
/**
* Resolve calls in the attribute layout and returns the number of backwards
* callables
*
* @param tokens -
* the attribute layout as a List of AttributeElements
*/
private void resolveCalls() {
for (int i = 0; i < attributeLayoutElements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
.get(i);
if (element instanceof Callable) {
Callable callable = (Callable) element;
List body = callable.body; // Look for calls in the body
for (int iIndex = 0; iIndex < body.size(); iIndex++) {
LayoutElement layoutElement = (LayoutElement) body
.get(iIndex);
// Set the callable for each call
resolveCallsForElement(i, callable, layoutElement);
}
}
}
int backwardsCallableIndex = 0;
for (int i = 0; i < attributeLayoutElements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
.get(i);
if (element instanceof Callable) {
Callable callable = (Callable) element;
if(callable.isBackwardsCallable) {
callable.setBackwardsCallableIndex(backwardsCallableIndex);
backwardsCallableIndex++;
}
}
}
backwardsCallCounts = new int[backwardsCallableIndex];
}
private void resolveCallsForElement(int i,
Callable currentCallable, LayoutElement layoutElement) {
if (layoutElement instanceof Call) {
Call call = (Call) layoutElement;
int index = call.callableIndex;
if (index == 0) { // Calls the parent callable
call.setCallable(currentCallable);
} else if (index > 0) { // Forwards call
for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
.get(k);
if (el instanceof Callable) {
index--;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
} else { // Backwards call
for (int k = i; k >= 0; k--) {
AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
.get(k);
if (el instanceof Callable) {
index++;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
}
} else if (layoutElement instanceof Replication) {
List children = ((Replication)layoutElement).layoutElements;
for (Iterator iterator = children.iterator(); iterator.hasNext();) {
LayoutElement object = (LayoutElement) iterator.next();
resolveCallsForElement(i, currentCallable, object);
}
}
}
private AttributeLayoutElement readNextAttributeElement(StringReader stream)
throws IOException {
stream.mark(1);
int nextChar = stream.read();
if (nextChar == -1) {
return null;
}
if (nextChar == '[') {
List body = readBody(getStreamUpToMatchingBracket(stream));
return new Callable(body);
} else {
stream.reset();
return readNextLayoutElement(stream);
}
}
private LayoutElement readNextLayoutElement(StringReader stream)
throws IOException {
int nextChar = stream.read();
if (nextChar == -1) {
return null;
}
switch (nextChar) {
// Integrals
case 'B':
case 'H':
case 'I':
case 'V':
return new Integral(new String(new char[] { (char)nextChar }));
case 'S':
case 'F':
return new Integral(new String(new char[] { (char)nextChar,
(char) stream.read() }));
case 'P':
stream.mark(1);
if (stream.read() != 'O') {
stream.reset();
lastPIntegral = new Integral("P" + (char) stream.read());
return lastPIntegral;
} else {
lastPIntegral = new Integral("PO" + (char) stream.read(), lastPIntegral);
return lastPIntegral;
}
case 'O':
stream.mark(1);
if (stream.read() != 'S') {
stream.reset();
return new Integral("O" + (char) stream.read(), lastPIntegral);
} else {
return new Integral("OS" + (char) stream.read(), lastPIntegral);
}
// Replication
case 'N':
char uint_type = (char) stream.read();
stream.read(); // '['
String str = readUpToMatchingBracket(stream);
return new Replication("" + uint_type, str);
// Union
case 'T':
String int_type = "" + (char) stream.read();
if (int_type.equals("S")) {
int_type += (char) stream.read();
}
List unionCases = new ArrayList();
UnionCase c;
while ((c = readNextUnionCase(stream)) != null) {
unionCases.add(c);
}
stream.read(); // '('
stream.read(); // ')'
stream.read(); // '['
List body = null;
stream.mark(1);
char next = (char) stream.read();
if (next != ']') {
stream.reset();
body = readBody(getStreamUpToMatchingBracket(stream));
}
return new Union(int_type, unionCases, body);
// Call
case '(':
int number = readNumber(stream).intValue();
stream.read(); // ')'
return new Call(number);
// Reference
case 'K':
case 'R':
String string = "" + (char)nextChar + (char) stream.read();
char nxt = (char) stream.read();
string += nxt;
if (nxt == 'N') {
string += (char) stream.read();
}
return new Reference(string);
}
return null;
}
/**
* Read a UnionCase from the stream
*
* @param stream
* @return
* @throws IOException
*/
private UnionCase readNextUnionCase(StringReader stream) throws IOException {
stream.mark(2);
stream.read(); // '('
char next = (char) stream.read();
if (next == ')') {
stream.reset();
return null;
} else {
stream.reset();
stream.read(); // '('
}
List tags = new ArrayList();
Integer nextTag;
do {
nextTag = readNumber(stream);
if(nextTag != null) {
tags.add(nextTag);
stream.read(); // ',' or ')'
}
} while (nextTag != null);
stream.read(); // '['
stream.mark(1);
next = (char) stream.read();
if (next == ']') {
return new UnionCase(tags);
} else {
stream.reset();
return new UnionCase(tags,
readBody(getStreamUpToMatchingBracket(stream)));
}
}
/**
* An AttributeLayoutElement is a part of an attribute layout and has one or
* more bands associated with it, which transmit the AttributeElement data
* for successive Attributes of this type.
*/
public interface AttributeLayoutElement {
public void addAttributeToBand(NewAttribute attribute, InputStream stream);
public void pack(OutputStream out) throws IOException, Pack200Exception;
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets);
}
public abstract class LayoutElement implements AttributeLayoutElement {
protected int getLength(char uint_type) {
int length = 0;
switch (uint_type) {
case 'B':
length = 1;
break;
case 'H':
length = 2;
break;
case 'I':
length = 4;
break;
case 'V':
length = 0;
break;
}
return length;
}
}
public class Integral extends LayoutElement {
private final String tag;
private final List band = new ArrayList();
private final BHSDCodec defaultCodec;
// used for bytecode offsets (OH and POH)
private Integral previousIntegral;
private int previousPValue;
public Integral(String tag) {
this.tag = tag;
this.defaultCodec = getCodec(tag);
}
public Integral(String tag, Integral previousIntegral) {
this.tag = tag;
this.defaultCodec = getCodec(tag);
this.previousIntegral = previousIntegral;
}
public String getTag() {
return tag;
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
Object val = null;
int value = 0;
if (tag.equals("B") || tag.equals("FB")) {
value = readInteger(1, stream) & 0xFF; // unsigned byte
} else if (tag.equals("SB")) {
value = readInteger(1, stream);
} else if (tag.equals("H") || tag.equals("FH")) {
value = readInteger(2, stream) & 0xFFFF; // unsigned short
} else if (tag.equals("SH")) {
value = readInteger(2, stream);
} else if (tag.equals("I") || tag.equals("FI")) {
value = readInteger(4, stream);
} else if (tag.equals("SI")) {
value = readInteger(4, stream);
} else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
// Not currently supported
} else if (tag.startsWith("PO") || tag.startsWith("OS")) {
char uint_type = tag.substring(2).toCharArray()[0];
int length = getLength(uint_type);
value = readInteger(length, stream);
value += previousIntegral.previousPValue;
val = attribute.getLabel(value);
previousPValue = value;
} else if (tag.startsWith("P")) {
char uint_type = tag.substring(1).toCharArray()[0];
int length = getLength(uint_type);
value = readInteger(length, stream);
val = attribute.getLabel(value);
previousPValue = value;
} else if (tag.startsWith("O")) {
char uint_type = tag.substring(1).toCharArray()[0];
int length = getLength(uint_type);
value = readInteger(length, stream);
value += previousIntegral.previousPValue;
val = attribute.getLabel(value);
previousPValue = value;
}
if(val == null) {
val = new Integer(value);
}
band.add(val);
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
PackingUtils.log("Writing new attribute bands...");
byte[] encodedBand = encodeBandInt(tag, integerListToArray(band),
defaultCodec);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ tag + "[" + band.size() + "]");
}
public int latestValue() {
return ((Integer)band.get(band.size() - 1)).intValue();
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
if(tag.startsWith("O") || tag.startsWith("PO")) {
renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
} else if (tag.startsWith("P")) {
for (int i = band.size() - 1; i >= 0; i--) {
Object label = band.get(i);
if (label instanceof Integer) {
break;
} else if (label instanceof Label) {
band.remove(i);
Integer bytecodeIndex = (Integer) labelsToOffsets
.get(label);
band.add(i, new Integer(bciRenumbering.get(bytecodeIndex
.intValue())));
}
}
}
}
private void renumberOffsetBci(List relative,
IntList bciRenumbering, Map labelsToOffsets) {
for (int i = band.size() - 1; i >= 0; i--) {
Object label = band.get(i);
if (label instanceof Integer) {
break;
} else if (label instanceof Label) {
band.remove(i);
Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
Integer renumberedOffset = new Integer(bciRenumbering
.get(bytecodeIndex.intValue())
- ((Integer) relative.get(i)).intValue());
band.add(i, renumberedOffset);
}
}
}
}
/**
* A replication is an array of layout elements, with an associated count
*/
public class Replication extends LayoutElement {
private final Integral countElement;
private final List layoutElements = new ArrayList();
public Integral getCountElement() {
return countElement;
}
public List getLayoutElements() {
return layoutElements;
}
public Replication(String tag, String contents) throws IOException {
this.countElement = new Integral(tag);
StringReader stream = new StringReader(contents);
LayoutElement e;
while ((e = readNextLayoutElement(stream)) != null) {
layoutElements.add(e);
}
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
countElement.addAttributeToBand(attribute, stream);
int count = countElement.latestValue();
for (int i = 0; i < count; i++) {
for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.addAttributeToBand(attribute, stream);
}
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
countElement.pack(out);
for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.pack(out);
}
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
}
}
}
/**
* A Union is a type of layout element where the tag value acts as a
* selector for one of the union cases
*/
public class Union extends LayoutElement {
private final Integral unionTag;
private final List unionCases;
private final List defaultCaseBody;
public Union(String tag, List unionCases, List body) {
this.unionTag = new Integral(tag);
this.unionCases = unionCases;
this.defaultCaseBody = body;
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
unionTag.addAttributeToBand(attribute, stream);
long tag = unionTag.latestValue();
boolean defaultCase = true;
for (int i = 0; i < unionCases.size(); i++) {
UnionCase element = (UnionCase) unionCases.get(i);
if (element.hasTag(tag)) {
defaultCase = false;
element.addAttributeToBand(attribute, stream);
}
}
if (defaultCase) {
for (int i = 0; i < defaultCaseBody.size(); i++) {
LayoutElement element = (LayoutElement) defaultCaseBody
.get(i);
element.addAttributeToBand(attribute, stream);
}
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
unionTag.pack(out);
for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
UnionCase unionCase = (UnionCase) iterator.next();
unionCase.pack(out);
}
for (Iterator iterator = defaultCaseBody.iterator(); iterator
.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
.next();
layoutElement.pack(out);
}
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
UnionCase unionCase = (UnionCase) iterator.next();
unionCase.renumberBci(bciRenumbering, labelsToOffsets);
}
for (Iterator iterator = defaultCaseBody.iterator(); iterator
.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
.next();
layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
}
}
public Integral getUnionTag() {
return unionTag;
}
public List getUnionCases() {
return unionCases;
}
public List getDefaultCaseBody() {
return defaultCaseBody;
}
}
public class Call extends LayoutElement {
private final int callableIndex;
private Callable callable;
public Call(int callableIndex) {
this.callableIndex = callableIndex;
}
public void setCallable(Callable callable) {
this.callable = callable;
if (callableIndex < 1) {
callable.setBackwardsCallable();
}
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
callable.addAttributeToBand(attribute, stream);
if(callableIndex < 1) {
callable.addBackwardsCall();
}
}
public void pack(OutputStream out) {
// do nothing here as pack will be called on the callable at another time
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
// do nothing here as renumberBci will be called on the callable at another time
}
public int getCallableIndex() {
return callableIndex;
}
public Callable getCallable() {
return callable;
}
}
/**
* Constant Pool Reference
*/
public class Reference extends LayoutElement {
private final String tag;
private List band;
private final int length;
private boolean nullsAllowed = false;
public Reference(String tag) {
this.tag = tag;
length = getLength(tag.charAt(tag.length() - 1));
nullsAllowed = tag.indexOf('N') != -1;
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
int index = readInteger(4, stream);
if(tag.startsWith("RC")) { // Class
band.add(cpBands.getCPClass(attribute.readClass(index)));
} else if (tag.startsWith("RU")) { // UTF8 String
band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
} else if (tag.startsWith("RS")) { // Signature
band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
} else { // Constant
band.add(cpBands.getConstant(attribute.readConst(index)));
}
// TODO method and field references
}
public String getTag() {
return tag;
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
int[] ints;
if(nullsAllowed) {
ints = cpEntryOrNullListToArray(band);
} else {
ints = cpEntryListToArray(band);
}
byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
out.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ tag + "[" + ints.length + "]");
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
// nothing to do here
}
}
public class Callable implements AttributeLayoutElement {
private final List body;
private boolean isBackwardsCallable;
private int backwardsCallableIndex;
public Callable(List body) throws IOException {
this.body = body;
}
public void setBackwardsCallableIndex(int backwardsCallableIndex) {
this.backwardsCallableIndex = backwardsCallableIndex;
}
public void addBackwardsCall() {
backwardsCallCounts[backwardsCallableIndex]++;
}
public boolean isBackwardsCallable() {
return isBackwardsCallable;
}
/**
* Tells this Callable that it is a backwards callable
*/
public void setBackwardsCallable() {
this.isBackwardsCallable = true;
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
for (Iterator iterator = body.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.addAttributeToBand(attribute, stream);
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
for (Iterator iterator = body.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.pack(out);
}
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
for (Iterator iterator = body.iterator(); iterator.hasNext();) {
AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
}
}
public List getBody() {
return body;
}
}
/**
* A Union case
*/
public class UnionCase extends LayoutElement {
private final List body;
private final List tags;
public UnionCase(List tags) {
this.tags = tags;
this.body = Collections.EMPTY_LIST;
}
public boolean hasTag(long l) {
return tags.contains(new Integer((int) l));
}
public UnionCase(List tags, List body) throws IOException {
this.tags = tags;
this.body = body;
}
public void addAttributeToBand(NewAttribute attribute,
InputStream stream) {
for (int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.addAttributeToBand(attribute, stream);
}
}
public void pack(OutputStream out) throws IOException, Pack200Exception {
for (int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.pack(out);
}
}
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
for (int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.renumberBci(bciRenumbering, labelsToOffsets);
}
}
public List getBody() {
return body;
}
}
/**
* Utility method to get the contents of the given stream, up to the next
* ']', (ignoring pairs of brackets '[' and ']')
*
* @param stream
* @return
* @throws IOException
*/
private StringReader getStreamUpToMatchingBracket(StringReader stream)
throws IOException {
StringBuffer sb = new StringBuffer();
int foundBracket = -1;
while (foundBracket != 0) {
char c = (char) stream.read();
if (c == ']') {
foundBracket++;
}
if (c == '[') {
foundBracket--;
}
if (!(foundBracket == 0)) {
sb.append(c);
}
}
return new StringReader(sb.toString());
}
private int readInteger(int i, InputStream stream) {
int result = 0;
for (int j = 0; j < i; j++) {
try {
result = result << 8 | stream.read();
} catch (IOException e) {
throw new RuntimeException("Error reading unknown attribute");
}
}
// use casting to preserve sign
if(i == 1) result = (byte) result;
if(i == 2) result = (short) result;
return result;
}
/**
* Returns the {@link BHSDCodec} that should be used for the given layout
* element
*
* @param layoutElement
*/
private BHSDCodec getCodec(String layoutElement) {
if (layoutElement.indexOf('O') >= 0) {
return Codec.BRANCH5;
} else if (layoutElement.indexOf('P') >= 0) {
return Codec.BCI5;
} else if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$
&& layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
return Codec.SIGNED5;
} else if (layoutElement.indexOf('B') >= 0) {
return Codec.BYTE1;
} else {
return Codec.UNSIGNED5;
}
}
/**
* Utility method to get the contents of the given stream, up to the next
* ']', (ignoring pairs of brackets '[' and ']')
*
* @param stream
* @return
* @throws IOException
*/
private String readUpToMatchingBracket(StringReader stream)
throws IOException {
StringBuffer sb = new StringBuffer();
int foundBracket = -1;
while (foundBracket != 0) {
char c = (char) stream.read();
if (c == ']') {
foundBracket++;
}
if (c == '[') {
foundBracket--;
}
if (!(foundBracket == 0)) {
sb.append(c);
}
}
return sb.toString();
}
/**
* Read a number from the stream and return it
*
* @param stream
* @return
* @throws IOException
*/
private Integer readNumber(StringReader stream) throws IOException {
stream.mark(1);
char first = (char) stream.read();
boolean negative = first == '-';
if (!negative) {
stream.reset();
}
stream.mark(100);
int i;
int length = 0;
while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) {
length++;
}
stream.reset();
if(length == 0) {
return null;
}
char[] digits = new char[length];
int read = stream.read(digits);
if (read != digits.length) {
throw new IOException("Error reading from the input stream");
}
return new Integer(Integer.parseInt((negative ? "-" : "") + new String(digits)));
}
/**
* Read a 'body' section of the layout from the given stream
*
* @param stream
* @return List of LayoutElements
* @throws IOException
*/
private List readBody(StringReader stream) throws IOException {
List layoutElements = new ArrayList();
LayoutElement e;
while ((e = readNextLayoutElement(stream)) != null) {
layoutElements.add(e);
}
return layoutElements;
}
/**
* Renumber any bytecode indexes or offsets as described in section 5.5.2 of
* the pack200 specification
*
* @param bciRenumbering
* @param labelsToOffsets
*/
public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
AttributeLayoutElement element = (AttributeLayoutElement) iterator.next();
element.renumberBci(bciRenumbering, labelsToOffsets);
}
}
}

@ -0,0 +1,110 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
import org.objectweb.asm.ClassReader;
/**
* Wrapper for ClassReader that enables pack200 to obtain extra class file
* information
*/
public class Pack200ClassReader extends ClassReader {
private boolean lastConstantHadWideIndex;
private int lastUnsignedShort;
private boolean anySyntheticAttributes;
private String fileName;
/**
* @param b
* the contents of class file in the format of bytes
*/
public Pack200ClassReader(byte[] b) {
super(b);
}
/**
* @param is
* the input stream of class file
* @throws IOException
*/
public Pack200ClassReader(InputStream is) throws IOException {
super(is);
}
/**
* @param name
* @throws IOException
*/
public Pack200ClassReader(String name) throws IOException {
super(name);
}
/**
* @param b
* @param off
* @param len
*/
public Pack200ClassReader(byte[] b, int off, int len) {
super(b, off, len);
}
public int readUnsignedShort(int index) {
// Doing this to check whether last load-constant instruction was ldc (18) or ldc_w (19)
// TODO: Assess whether this impacts on performance
int unsignedShort = super.readUnsignedShort(index);
if(b[index - 1] == 19) {
lastUnsignedShort = unsignedShort;
} else {
lastUnsignedShort = Short.MIN_VALUE;
}
return unsignedShort;
}
public Object readConst(int item, char[] buf) {
lastConstantHadWideIndex = item == lastUnsignedShort;
return super.readConst(item, buf);
}
public String readUTF8(int arg0, char[] arg1) {
String utf8 = super.readUTF8(arg0, arg1);
if(!anySyntheticAttributes && "Synthetic".equals(utf8)) {
anySyntheticAttributes = true;
}
return utf8;
}
public boolean lastConstantHadWideIndex() {
return lastConstantHadWideIndex;
}
public boolean hasSyntheticAttributes() {
return anySyntheticAttributes;
}
public void setFileName(String name) {
this.fileName = name;
}
public String getFileName() {
return fileName;
}
}

@ -0,0 +1,58 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
/**
* Represents a problem with a Pack200 coding/decoding issue.
*/
public class Pack200Exception extends Exception {
private static final long serialVersionUID = 5168177401552611803L;
/**
* Create a new Pack200 exception with the given message
*
* @param message
* the text message to display
*/
public Pack200Exception(String message) {
super(message);
}
/**
* Create a new Pack200 exception with the given message and cause
*
* @param message
* the text message to display
* @param cause
* the {@link Throwable} that caused this problem
*/
public Pack200Exception(String message, Throwable cause) {
super(message, cause);
}
/**
* Create a new Pack200 exception with the given cause
*
* @param cause
* the {@link Throwable} that caused this problem
*/
public Pack200Exception(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,369 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Attribute;
/**
* Utility class to manage the various options available for pack200
*/
public class PackingOptions {
public static final String STRIP = "strip";
public static final String ERROR = "error";
public static final String PASS = "pass";
public static final String KEEP = "keep";
// All options are initially set to their defaults
private boolean repack = false;
private boolean gzip = true;
private boolean stripDebug = false;
private boolean keepFileOrder = true;
private long segmentLimit = 1000000L;
private int effort = 5;
private String deflateHint = KEEP;
private String modificationTime = KEEP;
private List passFiles;
private String unknownAttributeAction = PASS;
private Map classAttributeActions;
private Map fieldAttributeActions;
private Map methodAttributeActions;
private Map codeAttributeActions;
private boolean verbose = false;
private boolean quiet = true;
private String logFile;
private Attribute[] unknownAttributeTypes;
public boolean isRepack() {
return repack;
}
public void setRepack(boolean repack) {
this.repack = repack;
}
public boolean isGzip() {
return gzip;
}
public void setGzip(boolean gzip) {
this.gzip = gzip;
}
public boolean isStripDebug() {
return stripDebug;
}
/**
* Set strip debug attributes. If true, all debug attributes (i.e.
* LineNumberTable, SourceFile, LocalVariableTable and
* LocalVariableTypeTable attributes) are stripped when reading the input
* class files and not included in the output archive.
*
* @param stripDebug
*/
public void setStripDebug(boolean stripDebug) {
this.stripDebug = stripDebug;
}
public boolean isKeepFileOrder() {
return keepFileOrder;
}
public void setKeepFileOrder(boolean keepFileOrder) {
this.keepFileOrder = keepFileOrder;
}
public long getSegmentLimit() {
return segmentLimit;
}
/**
* Set the segment limit (equivalent to -S command line option)
* @param segmentLimit - the limit in bytes
*/
public void setSegmentLimit(long segmentLimit) {
this.segmentLimit = segmentLimit;
}
public int getEffort() {
return effort;
}
/**
* Set the compression effort level (0-9, equivalent to -E command line option)
* @param effort
*/
public void setEffort(int effort) {
this.effort = effort;
}
public String getDeflateHint() {
return deflateHint;
}
public boolean isKeepDeflateHint() {
return KEEP.equals(deflateHint);
}
public void setDeflateHint(String deflateHint) {
this.deflateHint = deflateHint;
}
public String getModificationTime() {
return modificationTime;
}
public void setModificationTime(String modificationTime) {
if (!KEEP.equals(modificationTime)
&& !"latest".equals(modificationTime)) {
throw new IllegalArgumentException(
"Bad argument: -m "
+ modificationTime
+ " ? transmit modtimes should be either latest or keep (default)");
}
this.modificationTime = modificationTime;
}
public boolean isPassFile(String passFileName) {
if (passFiles != null) {
for (Iterator iterator = passFiles.iterator(); iterator.hasNext();) {
String pass = (String) iterator.next();
if (passFileName.equals(pass)) {
return true;
} else if (!pass.endsWith(".class")) { // a whole directory is
// passed
if (!pass.endsWith("/")) {
// Make sure we don't get any false positives (e.g.
// exclude "org/apache/harmony/pack" should not match
// files under "org/apache/harmony/pack200/")
pass = pass + "/";
}
return passFileName.startsWith(pass);
}
}
}
return false;
}
/**
* Tell the compressor to pass the file with the given name, or if the name
* is a directory name all files under that directory will be passed.
*
* @param passFileName
* the file name
*/
public void addPassFile(String passFileName) {
if(passFiles == null) {
passFiles = new ArrayList();
}
String fileSeparator = System.getProperty("file.separator");
if(fileSeparator.equals("\\")) {
// Need to escape backslashes for replaceAll(), which uses regex
fileSeparator += "\\";
}
passFileName = passFileName.replaceAll(fileSeparator, "/");
passFiles.add(passFileName);
}
public void removePassFile(String passFileName) {
passFiles.remove(passFileName);
}
public String getUnknownAttributeAction() {
return unknownAttributeAction;
}
/**
* Tell the compressor what to do if an unknown attribute is encountered
* @param unknownAttributeAction - the action to perform
*/
public void setUnknownAttributeAction(String unknownAttributeAction) {
this.unknownAttributeAction = unknownAttributeAction;
if (!PASS.equals(unknownAttributeAction)
&& !ERROR.equals(unknownAttributeAction)
&& !STRIP.equals(unknownAttributeAction)) {
throw new RuntimeException("Incorrect option for -U, "
+ unknownAttributeAction);
}
}
public Map getClassAttributeActions() {
return classAttributeActions;
}
public void addClassAttributeAction(String attributeName, String action) {
if(classAttributeActions == null) {
classAttributeActions = new HashMap();
}
classAttributeActions.put(attributeName, action);
}
public Map getFieldAttributeActions() {
return fieldAttributeActions;
}
public void addFieldAttributeAction(String attributeName, String action) {
if(fieldAttributeActions == null) {
fieldAttributeActions = new HashMap();
}
fieldAttributeActions.put(attributeName, action);
}
public Map getMethodAttributeActions() {
return methodAttributeActions;
}
public void addMethodAttributeAction(String attributeName, String action) {
if(methodAttributeActions == null) {
methodAttributeActions = new HashMap();
}
methodAttributeActions.put(attributeName, action);
}
public Map getCodeAttributeActions() {
return codeAttributeActions;
}
public void addCodeAttributeAction(String attributeName, String action) {
if(codeAttributeActions == null) {
codeAttributeActions = new HashMap();
}
codeAttributeActions.put(attributeName, action);
}
public boolean isVerbose() {
return verbose;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public boolean isQuiet() {
return quiet;
}
public void setQuiet(boolean quiet) {
this.quiet = quiet;
}
public String getLogFile() {
return logFile;
}
public void setLogFile(String logFile) {
this.logFile = logFile;
}
private void addOrUpdateAttributeActions(List prototypes, Map attributeActions,
int tag) {
if (attributeActions != null) {
if (attributeActions.size() > 0) {
String name, action;
boolean prototypeExists;
NewAttribute newAttribute;
for (Iterator iteratorI = attributeActions.keySet().iterator(); iteratorI
.hasNext();) {
name = (String) iteratorI.next();
action = (String) attributeActions.get(name);
if (!ERROR.equals(action) && !STRIP.equals(action)
&& !PASS.equals(action)) {
prototypeExists = false;
for (Iterator iteratorJ = prototypes.iterator(); iteratorJ
.hasNext();) {
newAttribute = (NewAttribute) iteratorJ.next();
if (newAttribute.type.equals(name)) {
// if the attribute exists, update its context
newAttribute.addContext(tag);
prototypeExists = true;
break;
}
// if no attribute is found, add a new attribute
if (!prototypeExists) {
newAttribute = new NewAttribute(name, action,
tag);
prototypes.add(newAttribute);
}
}
}
}
}
}
}
public Attribute[] getUnknownAttributePrototypes() {
if (unknownAttributeTypes == null) {
List prototypes = new ArrayList();
addOrUpdateAttributeActions(prototypes, classAttributeActions,
AttributeDefinitionBands.CONTEXT_CLASS);
addOrUpdateAttributeActions(prototypes, methodAttributeActions,
AttributeDefinitionBands.CONTEXT_METHOD);
addOrUpdateAttributeActions(prototypes, fieldAttributeActions,
AttributeDefinitionBands.CONTEXT_FIELD);
addOrUpdateAttributeActions(prototypes, codeAttributeActions,
AttributeDefinitionBands.CONTEXT_CODE);
unknownAttributeTypes = (Attribute[]) prototypes
.toArray(new Attribute[0]);
}
return unknownAttributeTypes;
}
public String getUnknownClassAttributeAction(String type) {
String action = (String) classAttributeActions.get(type);
if(action == null) {
action = unknownAttributeAction;
}
return action;
}
public String getUnknownMethodAttributeAction(String type) {
String action = (String) methodAttributeActions.get(type);
if(action == null) {
action = unknownAttributeAction;
}
return action;
}
public String getUnknownFieldAttributeAction(String type) {
String action = (String) fieldAttributeActions.get(type);
if(action == null) {
action = unknownAttributeAction;
}
return action;
}
public String getUnknownCodeAttributeAction(String type) {
String action = (String) codeAttributeActions.get(type);
if(action == null) {
action = unknownAttributeAction;
}
return action;
}
}

@ -0,0 +1,250 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.apache.harmony.pack200.Archive.PackingFile;
public class PackingUtils {
private static PackingLogger packingLogger;
static {
packingLogger = new PackingLogger("org.harmony.apache.pack200", null);
LogManager.getLogManager().addLogger(packingLogger);
}
private static class PackingLogger extends Logger {
private boolean verbose = false;
protected PackingLogger(String name, String resourceBundleName) {
super(name, resourceBundleName);
}
public void log(LogRecord logRecord) {
if (verbose) {
super.log(logRecord);
}
}
public void setVerbose(boolean isVerbose) {
verbose = isVerbose;
}
}
public static void config(PackingOptions options) throws IOException {
String logFileName = options.getLogFile();
if (logFileName != null) {
FileHandler fileHandler = new FileHandler(logFileName, false);
fileHandler.setFormatter(new SimpleFormatter());
packingLogger.addHandler(fileHandler);
packingLogger.setUseParentHandlers(false);
}
packingLogger.setVerbose(options.isVerbose());
}
public static void log(String message) {
packingLogger.log(Level.INFO, message);
}
/**
* When effort is 0, the packer copys through the original jar input stream
* without compression
*
* @param jarInputStream
* the jar input stream
* @param jarOutputStream
* the jar output stream
* @throws IOException
*/
public static void copyThroughJar(JarInputStream jarInputStream,
OutputStream outputStream) throws IOException {
Manifest manifest = jarInputStream.getManifest();
JarOutputStream jarOutputStream = new JarOutputStream(outputStream,
manifest);
jarOutputStream.setComment("PACK200");
log("Packed " + JarFile.MANIFEST_NAME);
byte[] bytes = new byte[16384];
JarEntry jarEntry;
int bytesRead;
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
jarOutputStream.putNextEntry(jarEntry);
while ((bytesRead = jarInputStream.read(bytes)) != -1) {
jarOutputStream.write(bytes, 0, bytesRead);
}
log("Packed " + jarEntry.getName());
}
jarInputStream.close();
jarOutputStream.close();
}
/**
* When effort is 0, the packer copys through the original jar file without
* compression
*
* @param jarFile
* the input jar file
* @param jarOutputStream
* the jar output stream
* @throws IOException
*/
public static void copyThroughJar(JarFile jarFile, OutputStream outputStream)
throws IOException {
JarOutputStream jarOutputStream = new JarOutputStream(outputStream);
jarOutputStream.setComment("PACK200");
byte[] bytes = new byte[16384];
Enumeration entries = jarFile.entries();
InputStream inputStream;
JarEntry jarEntry;
int bytesRead;
while (entries.hasMoreElements()) {
jarEntry = (JarEntry) entries.nextElement();
jarOutputStream.putNextEntry(jarEntry);
inputStream = jarFile.getInputStream(jarEntry);
while ((bytesRead = inputStream.read(bytes)) != -1) {
jarOutputStream.write(bytes, 0, bytesRead);
}
jarOutputStream.closeEntry();
log("Packed " + jarEntry.getName());
}
jarFile.close();
jarOutputStream.close();
}
public static List getPackingFileListFromJar(JarInputStream jarInputStream,
boolean keepFileOrder) throws IOException {
List packingFileList = new ArrayList();
// add manifest file
Manifest manifest = jarInputStream.getManifest();
if (manifest != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
manifest.write(baos);
packingFileList.add(new PackingFile(JarFile.MANIFEST_NAME, baos
.toByteArray(), 0));
}
// add rest of entries in the jar
JarEntry jarEntry;
byte[] bytes;
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
bytes = readJarEntry(jarEntry, new BufferedInputStream(
jarInputStream));
packingFileList.add(new PackingFile(bytes, jarEntry));
}
// check whether it need reorder packing file list
if (!keepFileOrder) {
reorderPackingFiles(packingFileList);
}
return packingFileList;
}
public static List getPackingFileListFromJar(JarFile jarFile,
boolean keepFileOrder) throws IOException {
List packingFileList = new ArrayList();
Enumeration jarEntries = jarFile.entries();
JarEntry jarEntry;
byte[] bytes;
while (jarEntries.hasMoreElements()) {
jarEntry = (JarEntry) jarEntries.nextElement();
bytes = readJarEntry(jarEntry, new BufferedInputStream(jarFile
.getInputStream(jarEntry)));
packingFileList.add(new PackingFile(bytes, jarEntry));
}
// check whether it need reorder packing file list
if (!keepFileOrder) {
reorderPackingFiles(packingFileList);
}
return packingFileList;
}
private static byte[] readJarEntry(JarEntry jarEntry,
InputStream inputStream) throws IOException {
long size = jarEntry.getSize();
if (size > Integer.MAX_VALUE) {
// TODO: Should probably allow this
throw new RuntimeException("Large Class!");
} else if (size < 0) {
size = 0;
}
byte[] bytes = new byte[(int) size];
if (inputStream.read(bytes) != size) {
throw new RuntimeException("Error reading from stream");
}
return bytes;
}
private static void reorderPackingFiles(List packingFileList) {
Iterator iterator = packingFileList.iterator();
PackingFile packingFile;
while (iterator.hasNext()) {
packingFile = (PackingFile) iterator.next();
if (packingFile.isDirectory()) {
// remove directory entries
iterator.remove();
}
}
// Sort files by name, "META-INF/MANIFEST.MF" should be put in the 1st
// position
Collections.sort(packingFileList, new Comparator() {
public int compare(Object arg0, Object arg1) {
if (arg0 instanceof PackingFile && arg1 instanceof PackingFile) {
String fileName0 = ((PackingFile) arg0).getName();
String fileName1 = ((PackingFile) arg1).getName();
if (fileName0.equals(fileName1)) {
return 0;
} else if (JarFile.MANIFEST_NAME.equals(fileName0)) {
return -1;
} else if (JarFile.MANIFEST_NAME.equals(fileName1)) {
return 1;
}
return fileName0.compareTo(fileName1);
}
throw new IllegalArgumentException();
}
});
}
}

@ -0,0 +1,169 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
/**
* A PopulationCodec is a Codec that is well suited to encoding data that shows
* statistical or repetitive patterns, containing for example a few numbers
* which are repeated a lot throughout the set, but not necessarily
* sequentially.
*/
public class PopulationCodec extends Codec {
private final Codec favouredCodec;
private Codec tokenCodec;
private final Codec unfavouredCodec;
private int l;
private int[] favoured;
public PopulationCodec(Codec favouredCodec, Codec tokenCodec,
Codec unvafouredCodec) {
this.favouredCodec = favouredCodec;
this.tokenCodec = tokenCodec;
this.unfavouredCodec = unvafouredCodec;
}
public PopulationCodec(Codec favouredCodec, int l, Codec unfavouredCodec) {
if (l >= 256 || l <= 0) {
throw new IllegalArgumentException("L must be between 1..255");
}
this.favouredCodec = favouredCodec;
this.l = l;
this.unfavouredCodec = unfavouredCodec;
}
public int decode(InputStream in) throws IOException, Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public int decode(InputStream in, long last) throws IOException,
Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public int[] decodeInts(int n, InputStream in) throws IOException,
Pack200Exception {
lastBandLength = 0;
favoured = new int[n]; // there must be <= n values, but probably a lot
// less
int result[];
// read table of favorites first
int smallest = Integer.MAX_VALUE, absoluteSmallest;
int last = 0;
int value = 0, absoluteValue;
int k = -1;
while (true) {
value = favouredCodec.decode(in, last);
if (k > -1 && (value == smallest || value == last)) {
break;
}
favoured[++k] = value;
absoluteSmallest = Math.abs(smallest);
absoluteValue = Math.abs(value);
if (absoluteSmallest > absoluteValue) {
smallest = value;
} else if (absoluteSmallest == absoluteValue) {
// ensure that -X and +X -> +X
smallest = absoluteSmallest;
}
last = value;
}
lastBandLength += k;
// if tokenCodec needs to be derived from the T, L and K values
if (tokenCodec == null) {
if (k < 256) {
tokenCodec = Codec.BYTE1;
} else {
// if k >= 256, b >= 2
int b = 1;
BHSDCodec codec = null;
while (++b < 5) {
codec = new BHSDCodec(b, 256 - l, 0);
if (codec.encodes(k)) {
tokenCodec = codec;
break;
}
}
if (tokenCodec == null) {
throw new Pack200Exception(
"Cannot calculate token codec from " + k + " and "
+ l);
}
}
}
// read favorites
lastBandLength += n;
result = tokenCodec.decodeInts(n, in);
// read unfavorites
last = 0;
for (int i = 0; i < n; i++) {
int index = result[i];
if (index == 0) {
lastBandLength++;
result[i] = last = unfavouredCodec.decode(in, last);
} else {
result[i] = favoured[index - 1];
}
}
return result;
}
public int[] getFavoured() {
return favoured;
}
public Codec getFavouredCodec() {
return favouredCodec;
}
public Codec getUnfavouredCodec() {
return unfavouredCodec;
}
public byte[] encode(int value, int last) throws Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public byte[] encode(int value) throws Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public byte[] encode(int[] favoured, int[] tokens, int[] unfavoured) throws Pack200Exception {
byte[] favouredEncoded = favouredCodec.encode(favoured);
byte[] tokensEncoded = tokenCodec.encode(tokens);
byte[] unfavouredEncoded = unfavouredCodec.encode(unfavoured);
byte[] band = new byte[favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length];
return band;
}
public Codec getTokenCodec() {
return tokenCodec;
}
public int getL() {
return l;
}
}

@ -0,0 +1,158 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* A run codec is a grouping of two nested codecs; K values are decoded from the
* first codec, and the remaining codes are decoded from the remaining codec.
* Note that since this codec maintains state, the instances are not reusable.
*/
public class RunCodec extends Codec {
private int k;
private final Codec aCodec;
private final Codec bCodec;
private int last;
public RunCodec(int k, Codec aCodec, Codec bCodec) throws Pack200Exception {
if (k <= 0) {
throw new Pack200Exception(
"Cannot have a RunCodec for a negative number of numbers");
}
if (aCodec == null || bCodec == null) {
throw new Pack200Exception("Must supply both codecs for a RunCodec");
}
this.k = k;
this.aCodec = aCodec;
this.bCodec = bCodec;
}
public int decode(InputStream in) throws IOException, Pack200Exception {
return decode(in, this.last);
}
public int decode(InputStream in, long last) throws IOException,
Pack200Exception {
if (--k >= 0) {
int value = aCodec.decode(in, this.last);
this.last = (k == 0 ? 0 : value);
return normalise(value, aCodec);
} else {
this.last = bCodec.decode(in, this.last);
return normalise(this.last, bCodec);
}
}
private int normalise(int value, Codec codecUsed) {
if (codecUsed instanceof BHSDCodec) {
BHSDCodec bhsd = (BHSDCodec) codecUsed;
if (bhsd.isDelta()) {
long cardinality = bhsd.cardinality();
while (value > bhsd.largest()) {
value -= cardinality;
}
while (value < bhsd.smallest()) {
value += cardinality;
}
}
}
return value;
}
public int[] decodeInts(int n, InputStream in) throws IOException,
Pack200Exception {
int[] band = new int[n];
int[] aValues = aCodec.decodeInts(k, in);
normalise(aValues, aCodec);
int[] bValues = bCodec.decodeInts(n - k, in);
normalise(bValues, bCodec);
System.arraycopy(aValues, 0, band, 0, k);
System.arraycopy(bValues, 0, band, k, n - k);
lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength;
return band;
}
private void normalise(int[] band, Codec codecUsed) {
if (codecUsed instanceof BHSDCodec) {
BHSDCodec bhsd = (BHSDCodec) codecUsed;
if (bhsd.isDelta()) {
long cardinality = bhsd.cardinality();
for (int i = 0; i < band.length; i++) {
while (band[i] > bhsd.largest()) {
band[i] -= cardinality;
}
while (band[i] < bhsd.smallest()) {
band[i] += cardinality;
}
}
}
} else if (codecUsed instanceof PopulationCodec) {
PopulationCodec popCodec = (PopulationCodec) codecUsed;
int[] favoured = (int[]) popCodec.getFavoured().clone();
Arrays.sort(favoured);
for (int i = 0; i < band.length; i++) {
boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
Codec theCodec = favouredValue ? popCodec.getFavouredCodec()
: popCodec.getUnfavouredCodec();
if (theCodec instanceof BHSDCodec) {
BHSDCodec bhsd = (BHSDCodec) theCodec;
if (bhsd.isDelta()) {
long cardinality = bhsd.cardinality();
while (band[i] > bhsd.largest()) {
band[i] -= cardinality;
}
while (band[i] < bhsd.smallest()) {
band[i] += cardinality;
}
}
}
}
}
}
public String toString() {
return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec
+ "]";
}
public byte[] encode(int value, int last) throws Pack200Exception {
// TODO Auto-generated method stub
return null;
}
public byte[] encode(int value) throws Pack200Exception {
// TODO Auto-generated method stub
return null;
}
public int getK() {
return k;
}
public Codec getACodec() {
return aCodec;
}
public Codec getBCodec() {
return bCodec;
}
}

@ -0,0 +1,676 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.harmony.pack200.Archive.PackingFile;
import org.apache.harmony.pack200.Archive.SegmentUnit;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
/**
* A Pack200 archive consists of one or more Segments.
*/
public class Segment implements ClassVisitor {
private SegmentHeader segmentHeader;
private CpBands cpBands;
private AttributeDefinitionBands attributeDefinitionBands;
private IcBands icBands;
private ClassBands classBands;
private BcBands bcBands;
private FileBands fileBands;
private final SegmentFieldVisitor fieldVisitor = new SegmentFieldVisitor();
private final SegmentMethodVisitor methodVisitor = new SegmentMethodVisitor();
private Pack200ClassReader currentClassReader;
private PackingOptions options;
private boolean stripDebug;
private Attribute[] nonStandardAttributePrototypes;
/**
* The main method on Segment. Reads in all the class files, packs them and
* then writes the packed segment out to the given OutputStream.
*
* @param classes
* List of Pack200ClassReaders, one for each class file in the
* segment
* @param files
* List of Archive.Files, one for each file in the segment
* @param out
* the OutputStream to write the packed Segment to
* @param options
* packing options
* @throws IOException
* @throws Pack200Exception
*/
public void pack(SegmentUnit segmentUnit, OutputStream out, PackingOptions options)
throws IOException, Pack200Exception {
this.options = options;
this.stripDebug = options.isStripDebug();
int effort = options.getEffort();
nonStandardAttributePrototypes = options.getUnknownAttributePrototypes();
PackingUtils.log("Start to pack a new segment with "
+ segmentUnit.fileListSize() + " files including "
+ segmentUnit.classListSize() + " classes");
PackingUtils.log("Initialize a header for the segment");
segmentHeader = new SegmentHeader();
segmentHeader.setFile_count(segmentUnit.fileListSize());
segmentHeader.setHave_all_code_flags(!stripDebug);
if (!options.isKeepDeflateHint()) {
segmentHeader.setDeflate_hint("true".equals(options
.getDeflateHint()));
}
PackingUtils.log("Setup constant pool bands for the segment");
cpBands = new CpBands(this, effort);
PackingUtils.log("Setup attribute definition bands for the segment");
attributeDefinitionBands = new AttributeDefinitionBands(this, effort, nonStandardAttributePrototypes);
PackingUtils.log("Setup internal class bands for the segment");
icBands = new IcBands(segmentHeader, cpBands, effort);
PackingUtils.log("Setup class bands for the segment");
classBands = new ClassBands(this, segmentUnit.classListSize(), effort, stripDebug);
PackingUtils.log("Setup byte code bands for the segment");
bcBands = new BcBands(cpBands, this, effort);
PackingUtils.log("Setup file bands for the segment");
fileBands = new FileBands(cpBands, segmentHeader, options, segmentUnit, effort);
processClasses(segmentUnit);
cpBands.finaliseBands();
attributeDefinitionBands.finaliseBands();
icBands.finaliseBands();
classBands.finaliseBands();
bcBands.finaliseBands();
fileBands.finaliseBands();
// Using a temporary stream because we have to pack the other bands
// before segmentHeader because the band_headers band is only created
// when the other bands are packed, but comes before them in the packed
// file.
ByteArrayOutputStream bandsOutputStream = new ByteArrayOutputStream();
PackingUtils.log("Packing...");
cpBands.pack(bandsOutputStream);
attributeDefinitionBands.pack(bandsOutputStream);
icBands.pack(bandsOutputStream);
classBands.pack(bandsOutputStream);
bcBands.pack(bandsOutputStream);
fileBands.pack(bandsOutputStream);
ByteArrayOutputStream headerOutputStream = new ByteArrayOutputStream();
segmentHeader.pack(headerOutputStream);
headerOutputStream.writeTo(out);
bandsOutputStream.writeTo(out);
segmentUnit.addPackedByteAmount(headerOutputStream.size());
segmentUnit.addPackedByteAmount(bandsOutputStream.size());
PackingUtils.log("Wrote total of " + segmentUnit.getPackedByteAmount()
+ " bytes");
PackingUtils.log("Transmitted " + segmentUnit.fileListSize() + " files of "
+ segmentUnit.getByteAmount() + " input bytes in a segment of "
+ segmentUnit.getPackedByteAmount() + " bytes");
}
private void processClasses(SegmentUnit segmentUnit) throws Pack200Exception {
segmentHeader.setClass_count(segmentUnit.classListSize());
for (Iterator iterator = segmentUnit.getClassList().iterator(); iterator.hasNext();) {
Pack200ClassReader classReader = (Pack200ClassReader) iterator
.next();
currentClassReader = classReader;
int flags = 0;
if(stripDebug) {
flags |= ClassReader.SKIP_DEBUG;
}
try {
classReader.accept(this, flags);
} catch (PassException pe) {
// Pass this class through as-is rather than packing it
// TODO: probably need to deal with any inner classes
classBands.removeCurrentClass();
String name = classReader.getFileName();
boolean found = false;
for (Iterator iterator2 = segmentUnit.getFileList().iterator(); iterator2
.hasNext();) {
PackingFile file = (PackingFile) iterator2.next();
if(file.getName().equals(name)) {
found = true;
file.setContents(classReader.b);
break;
}
}
if(!found) {
throw new Pack200Exception("Error passing file " + name);
}
}
}
}
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
bcBands.setCurrentClass(name, superName);
segmentHeader.addMajorVersion(version);
classBands.addClass(version, access, name, signature, superName,
interfaces);
}
public void visitSource(String source, String debug) {
if(!stripDebug) {
classBands.addSourceFile(source);
}
}
public void visitOuterClass(String owner, String name, String desc) {
classBands.addEnclosingMethod(owner, name, desc);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_CLASS,
desc, visible);
}
public void visitAttribute(Attribute attribute) {
if(attribute.isUnknown()) {
String action = options.getUnknownAttributeAction();
if(action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
} else {
if(attribute instanceof NewAttribute) {
NewAttribute newAttribute = (NewAttribute) attribute;
if(newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CLASS)) {
String action = options.getUnknownClassAttributeAction(newAttribute.type);
if(action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
}
classBands.addClassAttribute(newAttribute);
} else {
throw new RuntimeException("Unexpected attribute encountered: " + attribute.type);
}
}
}
public void visitInnerClass(String name, String outerName,
String innerName, int flags) {
icBands.addInnerClass(name, outerName, innerName, flags);
}
public FieldVisitor visitField(int flags, String name, String desc,
String signature, Object value) {
classBands.addField(flags, name, desc, signature, value);
return fieldVisitor;
}
public MethodVisitor visitMethod(int flags, String name, String desc,
String signature, String[] exceptions) {
classBands.addMethod(flags, name, desc, signature, exceptions);
return methodVisitor;
}
public void visitEnd() {
classBands.endOfClass();
}
/**
* This class implements MethodVisitor to visit the contents and metadata
* related to methods in a class file.
*
* It delegates to BcBands for bytecode related visits and to ClassBands for
* everything else.
*/
public class SegmentMethodVisitor implements MethodVisitor {
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return new SegmentAnnotationVisitor(
MetadataBandGroup.CONTEXT_METHOD, desc, visible);
}
public AnnotationVisitor visitAnnotationDefault() {
return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD);
}
public void visitAttribute(Attribute attribute) {
if(attribute.isUnknown()) {
String action = options.getUnknownAttributeAction();
if(action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
} else {
if(attribute instanceof NewAttribute) {
NewAttribute newAttribute = (NewAttribute) attribute;
if (attribute.isCodeAttribute()) {
if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CODE)) {
String action = options
.getUnknownCodeAttributeAction(newAttribute.type);
if (action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
}
classBands.addCodeAttribute(newAttribute);
} else {
if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_METHOD)) {
String action = options
.getUnknownMethodAttributeAction(newAttribute.type);
if (action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
}
classBands.addMethodAttribute(newAttribute);
}
} else {
throw new RuntimeException("Unexpected attribute encountered: " + attribute.type);
}
}
}
public void visitCode() {
classBands.addCode();
}
public void visitFrame(int arg0, int arg1, Object[] arg2, int arg3,
Object[] arg4) {
// TODO: Java 6 - implement support for this
}
public void visitLabel(Label label) {
bcBands.visitLabel(label);
}
public void visitLineNumber(int line, Label start) {
if(!stripDebug) {
classBands.addLineNumber(line, start);
}
}
public void visitLocalVariable(String name, String desc,
String signature, Label start, Label end, int index) {
if(!stripDebug) {
classBands.addLocalVariable(name, desc, signature, start, end,
index);
}
}
public void visitMaxs(int maxStack, int maxLocals) {
classBands.addMaxStack(maxStack, maxLocals);
}
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc, boolean visible) {
return new SegmentAnnotationVisitor(
MetadataBandGroup.CONTEXT_METHOD, parameter, desc, visible);
}
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
classBands.addHandler(start, end, handler, type);
}
public void visitEnd() {
classBands.endOfMethod();
bcBands.visitEnd();
}
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
bcBands.visitFieldInsn(opcode, owner, name, desc);
}
public void visitIincInsn(int var, int increment) {
bcBands.visitIincInsn(var, increment);
}
public void visitInsn(int opcode) {
bcBands.visitInsn(opcode);
}
public void visitIntInsn(int opcode, int operand) {
bcBands.visitIntInsn(opcode, operand);
}
public void visitJumpInsn(int opcode, Label label) {
bcBands.visitJumpInsn(opcode, label);
}
public void visitLdcInsn(Object cst) {
bcBands.visitLdcInsn(cst);
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
bcBands.visitLookupSwitchInsn(dflt, keys, labels);
}
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
bcBands.visitMethodInsn(opcode, owner, name, desc);
}
public void visitMultiANewArrayInsn(String desc, int dimensions) {
bcBands.visitMultiANewArrayInsn(desc, dimensions);
}
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label[] labels) {
bcBands.visitTableSwitchInsn(min, max, dflt, labels);
}
public void visitTypeInsn(int opcode, String type) {
bcBands.visitTypeInsn(opcode, type);
}
public void visitVarInsn(int opcode, int var) {
bcBands.visitVarInsn(opcode, var);
}
}
public ClassBands getClassBands() {
return classBands;
}
/**
* SegmentAnnotationVisitor implements <code>AnnotationVisitor</code> to
* visit Annotations found in a class file.
*/
public class SegmentAnnotationVisitor implements AnnotationVisitor {
private int context = -1;
private int parameter = -1;
private String desc;
private boolean visible;
private final List nameRU = new ArrayList();
private final List T = new ArrayList(); // tags
private final List values = new ArrayList();
private final List caseArrayN = new ArrayList();
private final List nestTypeRS = new ArrayList();
private final List nestNameRU = new ArrayList();
private final List nestPairN = new ArrayList();
public SegmentAnnotationVisitor(int context, String desc,
boolean visible) {
this.context = context;
this.desc = desc;
this.visible = visible;
}
public SegmentAnnotationVisitor(int context) {
this.context = context;
}
public SegmentAnnotationVisitor(int context, int parameter,
String desc, boolean visible) {
this.context = context;
this.parameter = parameter;
this.desc = desc;
this.visible = visible;
}
public void visit(String name, Object value) {
if (name == null) {
name = "";
}
nameRU.add(name);
values.add(value);
addTag(value);
}
private void addTag(Object value) {
if(value instanceof Integer) {
T.add("I");
} else if (value instanceof Double) {
T.add("D");
} else if (value instanceof Float) {
T.add("F");
} else if (value instanceof Long) {
T.add("J");
} else if (value instanceof Byte) {
T.add("B");
} else if (value instanceof Character) {
T.add("C");
} else if (value instanceof Short) {
T.add("S");
} else if (value instanceof Boolean) {
T.add("Z");
} else if (value instanceof String) {
T.add("s");
} else if (value instanceof Type) {
T.add("c");
}
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
T.add("@");
if (name == null) {
name = "";
}
nameRU.add(name);
nestTypeRS.add(desc);
nestPairN.add(new Integer(0));
return new AnnotationVisitor() {
public void visit(String name, Object value) {
Integer numPairs = (Integer) nestPairN.remove(nestPairN.size() - 1);
nestPairN.add(new Integer(numPairs.intValue() + 1));
nestNameRU.add(name);
values.add(value);
addTag(value);
}
public AnnotationVisitor visitAnnotation(String arg0,
String arg1) {
throw new RuntimeException("Not yet supported");
// return null;
}
public AnnotationVisitor visitArray(String arg0) {
throw new RuntimeException("Not yet supported");
// return null;
}
public void visitEnd() {
}
public void visitEnum(String name, String desc, String value) {
Integer numPairs = (Integer) nestPairN.remove(nestPairN.size() - 1);
nestPairN.add(new Integer(numPairs.intValue() + 1));
T.add("e");
nestNameRU.add(name);
values.add(desc);
values.add(value);
}
};
}
public AnnotationVisitor visitArray(String name) {
T.add("[");
if (name == null) {
name = "";
}
nameRU.add(name);
caseArrayN.add(new Integer(0));
return new AnnotationVisitor() {
public void visit(String name, Object value) {
Integer numCases = (Integer) caseArrayN.remove(caseArrayN.size() - 1);
caseArrayN.add(new Integer(numCases.intValue() + 1));
if (name == null) {
name = "";
}
nameRU.add(name);
values.add(value);
addTag(value);
}
public AnnotationVisitor visitAnnotation(String arg0,
String arg1) {
throw new RuntimeException("Not yet supported");
}
public AnnotationVisitor visitArray(String arg0) {
throw new RuntimeException("Not yet supported");
// return null;
}
public void visitEnd() {
}
public void visitEnum(String name, String desc, String value) {
Integer numCases = (Integer) caseArrayN.remove(caseArrayN.size() - 1);
caseArrayN.add(new Integer(numCases.intValue() + 1));
T.add("e");
if(name == null) {
name = "";
}
nameRU.add(name);
values.add(desc);
values.add(value);
}
};
}
public void visitEnd() {
if (desc == null) {
Segment.this.classBands.addAnnotationDefault(nameRU, T, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
} else if(parameter != -1) {
Segment.this.classBands.addParameterAnnotation(parameter, desc, visible, nameRU, T, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
} else {
Segment.this.classBands.addAnnotation(context, desc, visible, nameRU, T, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
}
}
public void visitEnum(String name, String desc, String value) {
T.add("e");
if (name == null) {
name = "";
}
nameRU.add(name);
values.add(desc);
values.add(value);
}
}
/**
* SegmentFieldVisitor implements <code>FieldVisitor</code> to visit the
* metadata relating to fields in a class file.
*/
public class SegmentFieldVisitor implements FieldVisitor {
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_FIELD,
desc, visible);
}
public void visitAttribute(Attribute attribute) {
if(attribute.isUnknown()) {
String action = options.getUnknownAttributeAction();
if(action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
} else {
if(attribute instanceof NewAttribute) {
NewAttribute newAttribute = (NewAttribute) attribute;
if(newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_FIELD)) {
String action = options.getUnknownFieldAttributeAction(newAttribute.type);
if(action.equals(PackingOptions.PASS)) {
passCurrentClass();
} else if (action.equals(PackingOptions.ERROR)) {
throw new Error("Unknown attribute encountered");
} // else skip
}
classBands.addFieldAttribute(newAttribute);
} else {
throw new RuntimeException("Unexpected attribute encountered: " + attribute.type);
}
}
}
public void visitEnd() {
}
}
public boolean lastConstantHadWideIndex() {
return currentClassReader.lastConstantHadWideIndex();
}
public CpBands getCpBands() {
return cpBands;
}
public SegmentHeader getSegmentHeader() {
return segmentHeader;
}
public AttributeDefinitionBands getAttrBands() {
return attributeDefinitionBands;
}
public IcBands getIcBands() {
return icBands;
}
public Pack200ClassReader getCurrentClassReader() {
return currentClassReader;
}
private void passCurrentClass() {
throw new PassException();
}
/**
* Exception indicating that the class currently being visited contains an
* unknown attribute, which means that by default the class file needs to be
* passed through as-is in the file_bands rather than being packed with
* pack200.
*/
public class PassException extends RuntimeException {
}
}

@ -0,0 +1,392 @@
/*
* 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.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.OutputStream;
/**
* SegmentHeader is the header band of a {@link Segment}. Corresponds to
* <code>segment_header</code> in the pack200 specification.
*/
public class SegmentHeader extends BandSet {
/**
* Create a new SegmentHeader
*/
public SegmentHeader() {
super(1, null); // Pass 1 for effort because bands in the segment header
// should always use the default encoding
}
private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
private static final int archive_minver = 7;
private static final int archive_majver = 150;
private int archive_options;
private int cp_Utf8_count;
private int cp_Int_count;
private int cp_Float_count;
private int cp_Long_count;
private int cp_Double_count;
private int cp_String_count;
private int cp_Class_count;
private int cp_Signature_count;
private int cp_Descr_count;
private int cp_Field_count;
private int cp_Method_count;
private int cp_Imethod_count;
private int attribute_definition_count;
private final IntList band_headers = new IntList();
private boolean have_all_code_flags = true; // true by default
private int archive_size_hi;
private int archive_size_lo;
private int archive_next_count;
private int archive_modtime;
private int file_count;
private boolean deflate_hint;
private boolean have_file_modtime = true;
private boolean have_file_options = true;
private boolean have_file_size_hi;
private boolean have_class_flags_hi;
private boolean have_field_flags_hi;
private boolean have_method_flags_hi;
private boolean have_code_flags_hi;
private int ic_count;
private int class_count;
private final Counter majverCounter = new Counter();
/**
* Encode and write the SegmentHeader bands to the OutputStream
*/
public void pack(OutputStream out) throws IOException, Pack200Exception {
out.write(encodeScalar(magic, Codec.BYTE1));
out.write(encodeScalar(archive_minver, Codec.UNSIGNED5));
out.write(encodeScalar(archive_majver, Codec.UNSIGNED5));
calculateArchiveOptions();
out.write(encodeScalar(archive_options, Codec.UNSIGNED5));
writeArchiveFileCounts(out);
writeArchiveSpecialCounts(out);
writeCpCounts(out);
writeClassCounts(out);
if (band_headers.size()> 0) {
out.write(encodeScalar(band_headers.toArray(), BHSDCodec.BYTE1));
}
}
private void calculateArchiveOptions() {
if (attribute_definition_count > 0 || band_headers.size() > 0) {
archive_options |= 1;
}
if (cp_Int_count > 0 || cp_Float_count > 0 || cp_Long_count > 0
|| cp_Double_count > 0) {
archive_options |= (1 << 1);
}
if (have_all_code_flags) {
archive_options |= (1 << 2);
}
if (file_count > 0) {
archive_options |= (1 << 4);
}
if (deflate_hint) {
archive_options |= (1 << 5);
}
if (have_file_modtime) {
archive_options |= (1 << 6);
}
if (have_file_options) {
archive_options |= (1 << 7);
}
if (have_file_size_hi) {
archive_options |= (1 << 8);
}
if (have_class_flags_hi) {
archive_options |= (1 << 9);
}
if (have_field_flags_hi) {
archive_options |= (1 << 10);
}
if (have_method_flags_hi) {
archive_options |= (1 << 11);
}
if (have_code_flags_hi) {
archive_options |= (1 << 12);
}
}
public void setCp_Utf8_count(int count) {
cp_Utf8_count = count;
}
public void setCp_Int_count(int count) {
cp_Int_count = count;
}
public void setCp_Float_count(int count) {
cp_Float_count = count;
}
public void setCp_Long_count(int count) {
cp_Long_count = count;
}
public void setCp_Double_count(int count) {
cp_Double_count = count;
}
public void setCp_String_count(int count) {
cp_String_count = count;
}
public void setCp_Class_count(int count) {
cp_Class_count = count;
}
public void setCp_Signature_count(int count) {
cp_Signature_count = count;
}
public void setCp_Descr_count(int count) {
cp_Descr_count = count;
}
public void setCp_Field_count(int count) {
cp_Field_count = count;
}
public void setCp_Method_count(int count) {
cp_Method_count = count;
}
public void setCp_Imethod_count(int count) {
cp_Imethod_count = count;
}
public void setAttribute_definition_count(int attribute_definition_count) {
this.attribute_definition_count = attribute_definition_count;
}
public void setHave_all_code_flags(boolean have_all_code_flags) {
this.have_all_code_flags = have_all_code_flags;
}
public void setArchive_size_hi(int archive_size_hi) {
this.archive_size_hi = archive_size_hi;
}
public void setArchive_size_lo(int archive_size_lo) {
this.archive_size_lo = archive_size_lo;
}
public void setArchive_next_count(int archive_next_count) {
this.archive_next_count = archive_next_count;
}
public void setArchive_modtime(int archive_modtime) {
this.archive_modtime = archive_modtime;
}
public int getArchive_modtime() {
return archive_modtime;
}
public void setFile_count(int file_count) {
this.file_count = file_count;
}
public void setDeflate_hint(boolean deflate_hint) {
this.deflate_hint = deflate_hint;
}
public void setHave_file_modtime(boolean have_file_modtime) {
this.have_file_modtime = have_file_modtime;
}
public void setHave_file_options(boolean have_file_options) {
this.have_file_options = have_file_options;
}
public void setHave_file_size_hi(boolean have_file_size_hi) {
this.have_file_size_hi = have_file_size_hi;
}
public void setHave_class_flags_hi(boolean have_class_flags_hi) {
this.have_class_flags_hi = have_class_flags_hi;
}
public void setHave_field_flags_hi(boolean have_field_flags_hi) {
this.have_field_flags_hi = have_field_flags_hi;
}
public void setHave_method_flags_hi(boolean have_method_flags_hi) {
this.have_method_flags_hi = have_method_flags_hi;
}
public void setHave_code_flags_hi(boolean have_code_flags_hi) {
this.have_code_flags_hi = have_code_flags_hi;
}
public boolean have_class_flags_hi() {
return have_class_flags_hi;
}
public boolean have_field_flags_hi() {
return have_field_flags_hi;
}
public boolean have_method_flags_hi() {
return have_method_flags_hi;
}
public boolean have_code_flags_hi() {
return have_code_flags_hi;
}
public void setIc_count(int ic_count) {
this.ic_count = ic_count;
}
public void setClass_count(int class_count) {
this.class_count = class_count;
}
private void writeCpCounts(OutputStream out) throws IOException,
Pack200Exception {
out.write(encodeScalar(cp_Utf8_count, Codec.UNSIGNED5));
if ((archive_options & (1 << 1)) != 0) { // have_cp_numbers
out.write(encodeScalar(cp_Int_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Float_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Long_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Double_count, Codec.UNSIGNED5));
}
out.write(encodeScalar(cp_String_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Class_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Signature_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Descr_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Field_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Method_count, Codec.UNSIGNED5));
out.write(encodeScalar(cp_Imethod_count, Codec.UNSIGNED5));
}
private void writeClassCounts(OutputStream out) throws IOException,
Pack200Exception {
int default_class_minver = 0;
int default_class_majver = majverCounter.getMostCommon();
out.write(encodeScalar(ic_count, Codec.UNSIGNED5));
out.write(encodeScalar(default_class_minver, Codec.UNSIGNED5));
out.write(encodeScalar(default_class_majver, Codec.UNSIGNED5));
out.write(encodeScalar(class_count, Codec.UNSIGNED5));
}
private void writeArchiveSpecialCounts(OutputStream out)
throws IOException, Pack200Exception {
if ((archive_options & 1) > 0) { // have_special_formats
out.write(encodeScalar(band_headers.size(), Codec.UNSIGNED5));
out.write(encodeScalar(attribute_definition_count,
Codec.UNSIGNED5));
}
}
private void writeArchiveFileCounts(OutputStream out) throws IOException,
Pack200Exception {
if ((archive_options & (1 << 4)) > 0) { // have_file_headers
out.write(encodeScalar(archive_size_hi, Codec.UNSIGNED5));
out.write(encodeScalar(archive_size_lo, Codec.UNSIGNED5));
out.write(encodeScalar(archive_next_count, Codec.UNSIGNED5));
out.write(encodeScalar(archive_modtime, Codec.UNSIGNED5));
out.write(encodeScalar(file_count, Codec.UNSIGNED5));
}
}
public void addMajorVersion(int major) {
majverCounter.add(major);
}
/**
* Counter for major/minor class file numbers so we can work out the default
*/
private class Counter {
private final int[] objs = new int[8];
private final int[] counts = new int[8];
private int length;
public void add(int obj) {
boolean found = false;
for (int i = 0; i < length; i++) {
if (objs[i] == obj) {
counts[i]++;
found = true;
}
}
if (!found) {
objs[length] = obj;
counts[length] = 1;
length++;
if (length > objs.length - 1) {
Object[] newArray = new Object[objs.length + 8];
System.arraycopy(objs, 0, newArray, 0, length);
}
}
}
public int getMostCommon() {
int returnIndex = 0;
for (int i = 0; i < length; i++) {
if (counts[i] > counts[returnIndex]) {
returnIndex = i;
}
}
return objs[returnIndex];
}
}
public int getDefaultMinorVersion() {
return 0;
}
public int getDefaultMajorVersion() {
return majverCounter.getMostCommon();
}
public boolean have_file_size_hi() {
return have_file_size_hi;
}
public boolean have_file_modtime() {
return have_file_modtime;
}
public boolean have_file_options() {
return have_file_options;
}
public boolean have_all_code_flags() {
return have_all_code_flags;
}
public void appendBandCodingSpecifier(int specifier) {
band_headers.add(specifier);
}
}

@ -0,0 +1,245 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.GZIPInputStream;
import org.apache.harmony.pack200.Pack200Exception;
/**
* Archive is the main entry point to unpack200. An archive is constructed with
* either two file names, a pack file and an output file name or an input stream
* and an output streams. Then <code>unpack()</code> is called, to unpack the
* pack200 archive.
*/
public class Archive {
private InputStream inputStream;
private final JarOutputStream outputStream;
private boolean removePackFile;
private int logLevel = Segment.LOG_LEVEL_STANDARD;
private FileOutputStream logFile;
private boolean overrideDeflateHint;
private boolean deflateHint;
private String inputFileName;
private String outputFileName;
/**
* Creates an Archive with the given input and output file names.
*
* @param inputFile
* @param outputFile
* @throws FileNotFoundException
* if the input file does not exist
* @throws IOException
*/
public Archive(String inputFile, String outputFile)
throws FileNotFoundException, IOException {
this.inputFileName = inputFile;
this.outputFileName = outputFile;
inputStream = new FileInputStream(inputFile);
outputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
}
/**
* Creates an Archive with streams for the input and output files. Note: If
* you use this method then calling {@link #setRemovePackFile(boolean)} will
* have no effect.
*
* @param inputStream
* @param outputStream
* @throws IOException
*/
public Archive(InputStream inputStream, JarOutputStream outputStream)
throws IOException {
this.inputStream = inputStream;
this.outputStream = outputStream;
}
/**
* Unpacks the Archive from the input file to the output file
*
* @throws Pack200Exception
* @throws IOException
*/
public void unpack() throws Pack200Exception, IOException {
outputStream.setComment("PACK200");
try {
if (!inputStream.markSupported()) {
inputStream = new BufferedInputStream(inputStream);
if (!inputStream.markSupported())
throw new IllegalStateException();
}
inputStream.mark(2);
if (((inputStream.read() & 0xFF) | (inputStream.read() & 0xFF) << 8) == GZIPInputStream.GZIP_MAGIC) {
inputStream.reset();
inputStream = new BufferedInputStream(new GZIPInputStream(
inputStream));
} else {
inputStream.reset();
}
inputStream.mark(4);
int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D }; // Magic word for
// pack200
int word[] = new int[4];
for (int i = 0; i < word.length; i++) {
word[i] = inputStream.read();
}
boolean compressedWithE0 = false;
for (int m = 0; m < magic.length; m++) {
if (word[m] != magic[m]) {
compressedWithE0 = true;
}
}
inputStream.reset();
if (compressedWithE0) { // The original Jar was not packed, so just
// copy it across
JarInputStream jarInputStream = new JarInputStream(inputStream);
JarEntry jarEntry;
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
outputStream.putNextEntry(jarEntry);
byte[] bytes = new byte[16384];
int bytesRead = jarInputStream.read(bytes);
while (bytesRead != -1) {
outputStream.write(bytes, 0, bytesRead);
bytesRead = jarInputStream.read(bytes);
}
outputStream.closeEntry();
}
} else {
int i = 0;
while (available(inputStream)) {
i++;
Segment segment = new Segment();
segment.setLogLevel(logLevel);
segment
.setLogStream(logFile != null ? (OutputStream) logFile
: (OutputStream) System.out);
segment.setPreRead(false);
if (i == 1) {
segment.log(Segment.LOG_LEVEL_VERBOSE,
"Unpacking from " + inputFileName + " to "
+ outputFileName);
}
segment.log(Segment.LOG_LEVEL_VERBOSE, "Reading segment "
+ i);
if (overrideDeflateHint) {
segment.overrideDeflateHint(deflateHint);
}
segment.unpack(inputStream, outputStream);
outputStream.flush();
if (inputStream instanceof FileInputStream) {
inputFileName = ((FileInputStream) inputStream).getFD()
.toString();
}
}
}
} finally {
try {
inputStream.close();
} catch (Exception e) {
}
try {
outputStream.close();
} catch (Exception e) {
}
if (logFile != null) {
try {
logFile.close();
} catch (Exception e) {
}
}
}
if (removePackFile) {
File file = new File(inputFileName);
boolean deleted = file.delete();
if (!deleted) {
throw new Pack200Exception("Failed to delete the input file.");
}
}
}
private boolean available(InputStream inputStream) throws IOException {
inputStream.mark(1);
int check = inputStream.read();
inputStream.reset();
return check != -1;
}
/**
* If removePackFile is set to true, the input file is deleted after
* unpacking
*
* @param removePackFile
*/
public void setRemovePackFile(boolean removePackFile) {
this.removePackFile = removePackFile;
}
public void setVerbose(boolean verbose) {
if (verbose) {
logLevel = Segment.LOG_LEVEL_VERBOSE;
} else if (logLevel == Segment.LOG_LEVEL_VERBOSE) {
logLevel = Segment.LOG_LEVEL_STANDARD;
}
}
public void setQuiet(boolean quiet) {
if (quiet) {
logLevel = Segment.LOG_LEVEL_QUIET;
} else if (logLevel == Segment.LOG_LEVEL_QUIET) {
logLevel = Segment.LOG_LEVEL_QUIET;
}
}
public void setLogFile(String logFileName) throws FileNotFoundException {
this.logFile = new FileOutputStream(logFileName);
}
public void setLogFile(String logFileName, boolean append)
throws FileNotFoundException {
logFile = new FileOutputStream(logFileName, true);
}
public void setDeflateHint(boolean deflateHint) {
overrideDeflateHint = true;
this.deflateHint = deflateHint;
}
}

@ -0,0 +1,121 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.AnnotationDefaultAttribute;
import org.apache.harmony.unpack200.bytecode.CodeAttribute;
import org.apache.harmony.unpack200.bytecode.ConstantValueAttribute;
import org.apache.harmony.unpack200.bytecode.DeprecatedAttribute;
import org.apache.harmony.unpack200.bytecode.EnclosingMethodAttribute;
import org.apache.harmony.unpack200.bytecode.ExceptionsAttribute;
import org.apache.harmony.unpack200.bytecode.InnerClassesAttribute;
import org.apache.harmony.unpack200.bytecode.LineNumberTableAttribute;
import org.apache.harmony.unpack200.bytecode.LocalVariableTableAttribute;
import org.apache.harmony.unpack200.bytecode.LocalVariableTypeTableAttribute;
import org.apache.harmony.unpack200.bytecode.SignatureAttribute;
import org.apache.harmony.unpack200.bytecode.SourceFileAttribute;
/**
* Attribute definition bands are the set of bands used to define extra
* attributes transmitted in the archive.
*/
public class AttrDefinitionBands extends BandSet {
private int[] attributeDefinitionHeader;
private String[] attributeDefinitionLayout;
private String[] attributeDefinitionName;
private AttributeLayoutMap attributeDefinitionMap;
private final String[] cpUTF8;
public AttrDefinitionBands(Segment segment) {
super(segment);
this.cpUTF8 = segment.getCpBands().getCpUTF8();
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
public void read(InputStream in) throws IOException, Pack200Exception {
int attributeDefinitionCount = header.getAttributeDefinitionCount();
attributeDefinitionHeader = decodeBandInt("attr_definition_headers",
in, Codec.BYTE1, attributeDefinitionCount);
attributeDefinitionName = parseReferences("attr_definition_name", in,
Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8);
attributeDefinitionLayout = parseReferences("attr_definition_layout",
in, Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8);
attributeDefinitionMap = new AttributeLayoutMap();
int overflowIndex = 32;
if (segment.getSegmentHeader().getOptions().hasClassFlagsHi()) {
overflowIndex = 63;
}
for (int i = 0; i < attributeDefinitionCount; i++) {
int context = attributeDefinitionHeader[i] & 0x03;
int index = (attributeDefinitionHeader[i] >> 2) - 1;
if (index == -1) {
index = overflowIndex++;
}
AttributeLayout layout = new AttributeLayout(
attributeDefinitionName[i], context,
attributeDefinitionLayout[i], index, false);
NewAttributeBands newBands = new NewAttributeBands(segment, layout);
attributeDefinitionMap.add(layout, newBands);
}
attributeDefinitionMap.checkMap();
setupDefaultAttributeNames();
}
public void unpack() throws Pack200Exception, IOException {
}
private void setupDefaultAttributeNames() {
AnnotationDefaultAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("AnnotationDefault")); //$NON-NLS-1$
CodeAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("Code")); //$NON-NLS-1$
ConstantValueAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("ConstantValue")); //$NON-NLS-1$
DeprecatedAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("Deprecated")); //$NON-NLS-1$
EnclosingMethodAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("EnclosingMethod")); //$NON-NLS-1$
ExceptionsAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("Exceptions")); //$NON-NLS-1$
InnerClassesAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("InnerClasses")); //$NON-NLS-1$
LineNumberTableAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("LineNumberTable")); //$NON-NLS-1$
LocalVariableTableAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("LocalVariableTable")); //$NON-NLS-1$
LocalVariableTypeTableAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("LocalVariableTypeTable")); //$NON-NLS-1$
SignatureAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("Signature")); //$NON-NLS-1$
SourceFileAttribute.setAttributeName(segment.getCpBands().cpUTF8Value("SourceFile")); //$NON-NLS-1$
MetadataBandGroup.setRvaAttributeName(segment.getCpBands().cpUTF8Value("RuntimeVisibleAnnotations"));
MetadataBandGroup.setRiaAttributeName(segment.getCpBands().cpUTF8Value("RuntimeInvisibleAnnotations"));
MetadataBandGroup.setRvpaAttributeName(segment.getCpBands().cpUTF8Value("RuntimeVisibleParameterAnnotations"));
MetadataBandGroup.setRipaAttributeName(segment.getCpBands().cpUTF8Value("RuntimeInvisibleParameterAnnotations"));
}
public AttributeLayoutMap getAttributeDefinitionMap() {
return attributeDefinitionMap;
}
}

@ -0,0 +1,286 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.ClassFileEntry;
/**
* AttributeLayout defines a layout that describes how an attribute will be
* transmitted.
*/
public class AttributeLayout implements IMatcher {
public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$
public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$
public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$
public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$
public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$
public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$
public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$
public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$
public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$
public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$
public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$
public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$
public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$
public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$
public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$
public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$
public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$
public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$
public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$
public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$
public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$
public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$
public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$
public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$
public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$
public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$
public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$
public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$
public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$
public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$
public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$
public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$
public static final int CONTEXT_CLASS = 0;
public static final int CONTEXT_CODE = 3;
public static final int CONTEXT_FIELD = 1;
public static final int CONTEXT_METHOD = 2;
public static final String[] contextNames = { "Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"Code", }; //$NON-NLS-1$
private static ClassFileEntry getValue(String layout, long value,
SegmentConstantPool pool) throws Pack200Exception {
if (layout.startsWith("R")) { //$NON-NLS-1$
// references
if (layout.indexOf('N') != -1)
value--;
if (layout.startsWith("RU")) { //$NON-NLS-1$
return pool.getValue(SegmentConstantPool.UTF_8, value);
} else if (layout.startsWith("RS")) { //$NON-NLS-1$
return pool.getValue(SegmentConstantPool.SIGNATURE, value);
}
} else if (layout.startsWith("K")) { //$NON-NLS-1$
char type = layout.charAt(1);
switch (type) {
case 'S': // String
return pool.getValue(SegmentConstantPool.CP_STRING, value);
case 'I': // Int (or byte or short)
case 'C': // Char
return pool.getValue(SegmentConstantPool.CP_INT, value);
case 'F': // Float
return pool.getValue(SegmentConstantPool.CP_FLOAT, value);
case 'J': // Long
return pool.getValue(SegmentConstantPool.CP_LONG, value);
case 'D': // Double
return pool.getValue(SegmentConstantPool.CP_DOUBLE, value);
}
}
throw new Pack200Exception("Unknown layout encoding: " + layout);
}
private final int context;
private final int index;
private final String layout;
private long mask;
private final String name;
private final boolean isDefault;
private int backwardsCallCount;
/**
* Construct a default AttributeLayout (equivalent to
* <code>new AttributeLayout(name, context, layout, index, true);</code>)
*
* @param name
* @param context
* @param layout
* @param index
* @throws Pack200Exception
*/
public AttributeLayout(String name, int context, String layout, int index)
throws Pack200Exception {
this(name, context, layout, index, true);
}
public AttributeLayout(String name, int context, String layout, int index,
boolean isDefault) throws Pack200Exception {
super();
this.index = index;
this.context = context;
if (index >= 0) {
this.mask = 1L << index;
} else {
this.mask = 0;
}
if (context != CONTEXT_CLASS && context != CONTEXT_CODE
&& context != CONTEXT_FIELD && context != CONTEXT_METHOD)
throw new Pack200Exception("Attribute context out of range: "
+ context);
if (layout == null) // || layout.length() == 0)
throw new Pack200Exception("Cannot have a null layout");
if (name == null || name.length() == 0)
throw new Pack200Exception("Cannot have an unnamed layout");
this.name = name;
this.layout = layout;
this.isDefault = isDefault;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final AttributeLayout other = (AttributeLayout) obj;
if (layout == null) {
if (other.layout != null)
return false;
} else if (!layout.equals(other.layout))
return false;
if (index != other.index)
return false;
if (context != other.context)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public Codec getCodec() {
if (layout.indexOf('O') >= 0) {
return Codec.BRANCH5;
} else if (layout.indexOf('P') >= 0) {
return Codec.BCI5;
} else if (layout.indexOf('S') >= 0 && layout.indexOf("KS") < 0 //$NON-NLS-1$
&& layout.indexOf("RS") < 0) { //$NON-NLS-1$
return Codec.SIGNED5;
} else if (layout.indexOf('B') >= 0) {
return Codec.BYTE1;
} else {
return Codec.UNSIGNED5;
}
}
public String getLayout() {
return layout;
}
public ClassFileEntry getValue(long value, SegmentConstantPool pool)
throws Pack200Exception {
return getValue(layout, value, pool);
}
public ClassFileEntry getValue(long value, String type, SegmentConstantPool pool)
throws Pack200Exception {
// TODO This really needs to be better tested, esp. the different types
// TODO This should have the ability to deal with RUN stuff too, and
// unions
if (layout.startsWith("KQ")) { //$NON-NLS-1$
if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
ClassFileEntry value2 = getValue("KS", value, pool); //$NON-NLS-1$
return value2;
} else {
return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
pool);
}
} else {
return getValue(layout, value, pool);
}
}
public int hashCode() {
int PRIME = 31;
int r = 1;
if (name != null) {
r = r * PRIME + name.hashCode();
}
if (layout != null) {
r = r * PRIME + layout.hashCode();
}
r = r * PRIME + index;
r = r * PRIME + context;
return r;
}
public boolean isClass() {
return context == CONTEXT_CLASS;
}
public boolean isCode() {
return context == CONTEXT_CODE;
}
public boolean isField() {
return context == CONTEXT_FIELD;
}
public boolean isMethod() {
return context == CONTEXT_METHOD;
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.IMatches#matches(long)
*/
public boolean matches(long value) {
return (value & mask) != 0;
}
public String toString() {
return contextNames[context] + ": " + name;
}
public int getContext() {
return context;
}
public int getIndex() {
return index;
}
public String getName() {
return name;
}
public int numBackwardsCallables() {
if (layout == "*") {
return 1;
} else {
return backwardsCallCount;
}
}
public boolean isDefaultLayout() {
return isDefault;
}
public void setBackwardsCallCount(int backwardsCallCount) {
this.backwardsCallCount = backwardsCallCount;
}
}

@ -0,0 +1,284 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.harmony.pack200.Pack200Exception;
/**
* Stores a mapping from attribute names to their corresponding layout types.
* Note that names of attribute layouts and their formats are <emph>not</emph>
* internationalized, and should not be translated.
*/
public class AttributeLayoutMap {
// Create all the default AttributeLayouts here
private static AttributeLayout[] getDefaultAttributeLayouts()
throws Pack200Exception {
return new AttributeLayout[] {
new AttributeLayout(AttributeLayout.ACC_PUBLIC,
AttributeLayout.CONTEXT_CLASS, "", 0),
new AttributeLayout(AttributeLayout.ACC_PUBLIC,
AttributeLayout.CONTEXT_FIELD, "", 0),
new AttributeLayout(AttributeLayout.ACC_PUBLIC,
AttributeLayout.CONTEXT_METHOD, "", 0),
new AttributeLayout(AttributeLayout.ACC_PRIVATE,
AttributeLayout.CONTEXT_CLASS, "", 1),
new AttributeLayout(AttributeLayout.ACC_PRIVATE,
AttributeLayout.CONTEXT_FIELD, "", 1),
new AttributeLayout(AttributeLayout.ACC_PRIVATE,
AttributeLayout.CONTEXT_METHOD, "", 1),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_LINE_NUMBER_TABLE,
AttributeLayout.CONTEXT_CODE, "NH[PHH]", 1),
new AttributeLayout(AttributeLayout.ACC_PROTECTED,
AttributeLayout.CONTEXT_CLASS, "", 2),
new AttributeLayout(AttributeLayout.ACC_PROTECTED,
AttributeLayout.CONTEXT_FIELD, "", 2),
new AttributeLayout(AttributeLayout.ACC_PROTECTED,
AttributeLayout.CONTEXT_METHOD, "", 2),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TABLE,
AttributeLayout.CONTEXT_CODE, "NH[PHOHRUHRSHH]", 2),
new AttributeLayout(AttributeLayout.ACC_STATIC,
AttributeLayout.CONTEXT_CLASS, "", 3),
new AttributeLayout(AttributeLayout.ACC_STATIC,
AttributeLayout.CONTEXT_FIELD, "", 3),
new AttributeLayout(AttributeLayout.ACC_STATIC,
AttributeLayout.CONTEXT_METHOD, "", 3),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE,
AttributeLayout.CONTEXT_CODE, "NH[PHOHRUHRSHH]", 3),
new AttributeLayout(AttributeLayout.ACC_FINAL,
AttributeLayout.CONTEXT_CLASS, "", 4),
new AttributeLayout(AttributeLayout.ACC_FINAL,
AttributeLayout.CONTEXT_FIELD, "", 4),
new AttributeLayout(AttributeLayout.ACC_FINAL,
AttributeLayout.CONTEXT_METHOD, "", 4),
new AttributeLayout(AttributeLayout.ACC_SYNCHRONIZED,
AttributeLayout.CONTEXT_CLASS, "", 5),
new AttributeLayout(AttributeLayout.ACC_SYNCHRONIZED,
AttributeLayout.CONTEXT_FIELD, "", 5),
new AttributeLayout(AttributeLayout.ACC_SYNCHRONIZED,
AttributeLayout.CONTEXT_METHOD, "", 5),
new AttributeLayout(AttributeLayout.ACC_VOLATILE,
AttributeLayout.CONTEXT_CLASS, "", 6),
new AttributeLayout(AttributeLayout.ACC_VOLATILE,
AttributeLayout.CONTEXT_FIELD, "", 6),
new AttributeLayout(AttributeLayout.ACC_VOLATILE,
AttributeLayout.CONTEXT_METHOD, "", 6),
new AttributeLayout(AttributeLayout.ACC_TRANSIENT,
AttributeLayout.CONTEXT_CLASS, "", 7),
new AttributeLayout(AttributeLayout.ACC_TRANSIENT,
AttributeLayout.CONTEXT_FIELD, "", 7),
new AttributeLayout(AttributeLayout.ACC_TRANSIENT,
AttributeLayout.CONTEXT_METHOD, "", 7),
new AttributeLayout(AttributeLayout.ACC_NATIVE,
AttributeLayout.CONTEXT_CLASS, "", 8),
new AttributeLayout(AttributeLayout.ACC_NATIVE,
AttributeLayout.CONTEXT_FIELD, "", 8),
new AttributeLayout(AttributeLayout.ACC_NATIVE,
AttributeLayout.CONTEXT_METHOD, "", 8),
new AttributeLayout(AttributeLayout.ACC_INTERFACE,
AttributeLayout.CONTEXT_CLASS, "", 9),
new AttributeLayout(AttributeLayout.ACC_INTERFACE,
AttributeLayout.CONTEXT_FIELD, "", 9),
new AttributeLayout(AttributeLayout.ACC_INTERFACE,
AttributeLayout.CONTEXT_METHOD, "", 9),
new AttributeLayout(AttributeLayout.ACC_ABSTRACT,
AttributeLayout.CONTEXT_CLASS, "", 10),
new AttributeLayout(AttributeLayout.ACC_ABSTRACT,
AttributeLayout.CONTEXT_FIELD, "", 10),
new AttributeLayout(AttributeLayout.ACC_ABSTRACT,
AttributeLayout.CONTEXT_METHOD, "", 10),
new AttributeLayout(AttributeLayout.ACC_STRICT,
AttributeLayout.CONTEXT_CLASS, "", 11),
new AttributeLayout(AttributeLayout.ACC_STRICT,
AttributeLayout.CONTEXT_FIELD, "", 11),
new AttributeLayout(AttributeLayout.ACC_STRICT,
AttributeLayout.CONTEXT_METHOD, "", 11),
new AttributeLayout(AttributeLayout.ACC_SYNTHETIC,
AttributeLayout.CONTEXT_CLASS, "", 12),
new AttributeLayout(AttributeLayout.ACC_SYNTHETIC,
AttributeLayout.CONTEXT_FIELD, "", 12),
new AttributeLayout(AttributeLayout.ACC_SYNTHETIC,
AttributeLayout.CONTEXT_METHOD, "", 12),
new AttributeLayout(AttributeLayout.ACC_ANNOTATION,
AttributeLayout.CONTEXT_CLASS, "", 13),
new AttributeLayout(AttributeLayout.ACC_ANNOTATION,
AttributeLayout.CONTEXT_FIELD, "", 13),
new AttributeLayout(AttributeLayout.ACC_ANNOTATION,
AttributeLayout.CONTEXT_METHOD, "", 13),
new AttributeLayout(AttributeLayout.ACC_ENUM,
AttributeLayout.CONTEXT_CLASS, "", 14),
new AttributeLayout(AttributeLayout.ACC_ENUM,
AttributeLayout.CONTEXT_FIELD, "", 14),
new AttributeLayout(AttributeLayout.ACC_ENUM,
AttributeLayout.CONTEXT_METHOD, "", 14),
new AttributeLayout(AttributeLayout.ATTRIBUTE_SOURCE_FILE,
AttributeLayout.CONTEXT_CLASS, "RUNH", 17),
new AttributeLayout(AttributeLayout.ATTRIBUTE_CONSTANT_VALUE,
AttributeLayout.CONTEXT_FIELD, "KQH", 17),
new AttributeLayout(AttributeLayout.ATTRIBUTE_CODE,
AttributeLayout.CONTEXT_METHOD, "", 17),
new AttributeLayout(AttributeLayout.ATTRIBUTE_ENCLOSING_METHOD,
AttributeLayout.CONTEXT_CLASS, "RCHRDNH", 18),
new AttributeLayout(AttributeLayout.ATTRIBUTE_EXCEPTIONS,
AttributeLayout.CONTEXT_METHOD, "NH[RCH]", 18),
new AttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
AttributeLayout.CONTEXT_CLASS, "RSH", 19),
new AttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
AttributeLayout.CONTEXT_FIELD, "RSH", 19),
new AttributeLayout(AttributeLayout.ATTRIBUTE_SIGNATURE,
AttributeLayout.CONTEXT_METHOD, "RSH", 19),
new AttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
AttributeLayout.CONTEXT_CLASS, "", 20),
new AttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
AttributeLayout.CONTEXT_FIELD, "", 20),
new AttributeLayout(AttributeLayout.ATTRIBUTE_DEPRECATED,
AttributeLayout.CONTEXT_METHOD, "", 20),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_CLASS, "*", 21),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_FIELD, "*", 21),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_METHOD, "*", 21),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_CLASS, "*", 22),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_FIELD, "*", 22),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS,
AttributeLayout.CONTEXT_METHOD, "*", 22),
new AttributeLayout(AttributeLayout.ATTRIBUTE_INNER_CLASSES,
AttributeLayout.CONTEXT_CLASS, "", 23),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
AttributeLayout.CONTEXT_METHOD, "*", 23),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_CLASS_FILE_VERSION,
AttributeLayout.CONTEXT_CLASS, "", 24),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS,
AttributeLayout.CONTEXT_METHOD, "*", 24),
new AttributeLayout(
AttributeLayout.ATTRIBUTE_ANNOTATION_DEFAULT,
AttributeLayout.CONTEXT_METHOD, "*", 25) };
}
private final Map classLayouts = new HashMap();
private final Map fieldLayouts = new HashMap();
private final Map methodLayouts = new HashMap();
private final Map codeLayouts = new HashMap();
// The order of the maps in this array should not be changed as their
// indices correspond to
// the value of their context constants (AttributeLayout.CONTEXT_CLASS etc.)
private final Map[] layouts = new Map[] { classLayouts, fieldLayouts,
methodLayouts, codeLayouts };
private final Map layoutsToBands = new HashMap();
public AttributeLayoutMap() throws Pack200Exception {
AttributeLayout[] defaultAttributeLayouts = getDefaultAttributeLayouts();
for (int i = 0; i < defaultAttributeLayouts.length; i++) {
add(defaultAttributeLayouts[i]);
}
}
public void add(AttributeLayout layout) {
layouts[layout.getContext()]
.put(new Integer(layout.getIndex()), layout);
}
public void add(AttributeLayout layout, NewAttributeBands newBands) {
add(layout);
layoutsToBands.put(layout, newBands);
}
public AttributeLayout getAttributeLayout(String name, int context)
throws Pack200Exception {
Map map = layouts[context];
for (Iterator iter = map.values().iterator(); iter.hasNext();) {
AttributeLayout layout = (AttributeLayout) iter.next();
if (layout.getName().equals(name)) {
return layout;
}
}
return null;
}
public AttributeLayout getAttributeLayout(int index, int context)
throws Pack200Exception {
Map map = layouts[context];
return (AttributeLayout) map.get(new Integer(index));
}
/**
* The map should not contain the same layout and name combination more than
* once for each context.
*
* @throws Pack200Exception
*
*/
public void checkMap() throws Pack200Exception {
for (int i = 0; i < layouts.length; i++) {
Map map = layouts[i];
Collection c = map.values();
if (!(c instanceof List)) {
c = new ArrayList(c);
}
List l = (List) c;
for (int j = 0; j < l.size(); j++) {
AttributeLayout layout1 = (AttributeLayout) l.get(j);
for (int j2 = j + 1; j2 < l.size(); j2++) {
AttributeLayout layout2 = (AttributeLayout) l.get(j2);
if (layout1.getName().equals(layout2.getName())
&& layout1.getLayout().equals(layout2.getLayout())) {
throw new Pack200Exception(
"Same layout/name combination: "
+ layout1.getLayout()
+ "/"
+ layout1.getName()
+ " exists twice for context: "
+ AttributeLayout.contextNames[layout1
.getContext()]);
}
}
}
}
}
public NewAttributeBands getAttributeBands(AttributeLayout layout) {
return (NewAttributeBands) layoutsToBands.get(layout);
}
}

@ -0,0 +1,551 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.apache.harmony.pack200.BHSDCodec;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.CodecEncoding;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.pack200.PopulationCodec;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.CPDouble;
import org.apache.harmony.unpack200.bytecode.CPFieldRef;
import org.apache.harmony.unpack200.bytecode.CPFloat;
import org.apache.harmony.unpack200.bytecode.CPInteger;
import org.apache.harmony.unpack200.bytecode.CPInterfaceMethodRef;
import org.apache.harmony.unpack200.bytecode.CPLong;
import org.apache.harmony.unpack200.bytecode.CPMethodRef;
import org.apache.harmony.unpack200.bytecode.CPNameAndType;
import org.apache.harmony.unpack200.bytecode.CPString;
import org.apache.harmony.unpack200.bytecode.CPUTF8;
/**
* Abstract superclass for a set of bands
*/
public abstract class BandSet {
public abstract void read(InputStream inputStream) throws IOException,
Pack200Exception;
public abstract void unpack() throws IOException, Pack200Exception;
public void unpack(InputStream in) throws IOException, Pack200Exception {
read(in);
unpack();
}
protected Segment segment;
protected SegmentHeader header;
public BandSet(Segment segment) {
this.segment = segment;
this.header = segment.getSegmentHeader();
}
/**
* Decode a band and return an array of <code>int</code> values
*
* @param name
* the name of the band (primarily for logging/debugging
* purposes)
* @param in
* the InputStream to decode from
* @param codec
* the default Codec for this band
* @param count
* the number of elements to read
* @return an array of decoded <code>int</code> values
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
public int[] decodeBandInt(String name, InputStream in, BHSDCodec codec,
int count) throws IOException, Pack200Exception {
int[] band;
// Useful for debugging
// if(count > 0) {
// System.out.println("decoding " + name + " " + count);
// }
Codec codecUsed = codec;
if (codec.getB() == 1 || count == 0) {
return codec.decodeInts(count, in);
}
int[] getFirst = codec.decodeInts(1, in);
if (getFirst.length == 0) {
return getFirst;
}
int first = getFirst[0];
if (codec.isSigned() && first >= -256 && first <= -1) {
// Non-default codec should be used
codecUsed = CodecEncoding.getCodec((-1 - first), header
.getBandHeadersInputStream(), codec);
band = codecUsed.decodeInts(count, in);
} else if (!codec.isSigned() && first >= codec.getL()
&& first <= codec.getL() + 255) {
// Non-default codec should be used
codecUsed = CodecEncoding.getCodec(first - codec.getL(), header
.getBandHeadersInputStream(), codec);
band = codecUsed.decodeInts(count, in);
} else {
// First element should not be discarded
band = codec.decodeInts(count - 1, in, first);
}
// Useful for debugging -E options:
//if(!codecUsed.equals(codec)) {
// int bytes = codecUsed.lastBandLength;
// System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes);
//}
if (codecUsed instanceof PopulationCodec) {
PopulationCodec popCodec = (PopulationCodec) codecUsed;
int[] favoured = (int[]) popCodec.getFavoured().clone();
Arrays.sort(favoured);
for (int i = 0; i < band.length; i++) {
boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
Codec theCodec = favouredValue ? popCodec.getFavouredCodec()
: popCodec.getUnfavouredCodec();
if (theCodec instanceof BHSDCodec
&& ((BHSDCodec) theCodec).isDelta()) {
BHSDCodec bhsd = (BHSDCodec) theCodec;
long cardinality = bhsd.cardinality();
while (band[i] > bhsd.largest()) {
band[i] -= cardinality;
}
while (band[i] < bhsd.smallest()) {
band[i] += cardinality;
}
}
}
}
return band;
}
/**
* Decode a band and return an array of <code>int[]</code> values
*
* @param name
* the name of the band (primarily for logging/debugging
* purposes)
* @param in
* the InputStream to decode from
* @param defaultCodec
* the default codec for this band
* @param counts
* the numbers of elements to read for each int array within the
* array to be returned
* @return an array of decoded <code>int[]</code> values
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
public int[][] decodeBandInt(String name, InputStream in,
BHSDCodec defaultCodec, int[] counts) throws IOException,
Pack200Exception {
int[][] result = new int[counts.length][];
int totalCount = 0;
for (int i = 0; i < counts.length; i++) {
totalCount += counts[i];
}
int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount);
int index = 0;
for (int i = 0; i < result.length; i++) {
result[i] = new int[counts[i]];
for (int j = 0; j < result[i].length; j++) {
result[i][j] = twoDResult[index];
index++;
}
}
return result;
}
public long[] parseFlags(String name, InputStream in, int count,
BHSDCodec codec, boolean hasHi) throws IOException,
Pack200Exception {
return parseFlags(name, in, new int[] { count },
(hasHi ? codec : null), codec)[0];
}
public long[][] parseFlags(String name, InputStream in, int counts[],
BHSDCodec codec, boolean hasHi) throws IOException,
Pack200Exception {
return parseFlags(name, in, counts, (hasHi ? codec : null), codec);
}
public long[] parseFlags(String name, InputStream in, int count,
BHSDCodec hiCodec, BHSDCodec loCodec) throws IOException,
Pack200Exception {
return parseFlags(name, in, new int[] { count }, hiCodec, loCodec)[0];
}
public long[][] parseFlags(String name, InputStream in, int counts[],
BHSDCodec hiCodec, BHSDCodec loCodec) throws IOException,
Pack200Exception {
int count = counts.length;
if (count == 0) {
return new long[][] { {} };
}
int sum = 0;
long[][] result = new long[count][];
for (int i = 0; i < count; i++) {
result[i] = new long[counts[i]];
sum += counts[i];
}
int[] hi = null;
int[] lo;
if (hiCodec != null) {
hi = decodeBandInt(name, in, hiCodec, sum);
lo = decodeBandInt(name, in, loCodec, sum);
} else {
lo = decodeBandInt(name, in, loCodec, sum);
}
int index = 0;
for (int i = 0; i < result.length; i++) {
for (int j = 0; j < result[i].length; j++) {
if (hi != null) {
result[i][j] = ((long) hi[index] << 32)
| (lo[index] & 4294967295L);
} else {
result[i][j] = lo[index];
}
index++;
}
}
return result;
}
/**
* Helper method to parse <i>count</i> references from <code>in</code>,
* using <code>codec</code> to decode the values as indexes into
* <code>reference</code> (which is populated prior to this call). An
* exception is thrown if a decoded index falls outside the range
* [0..reference.length-1].
*
* @param name
* the band name
* @param in
* the input stream to read from
* @param codec
* the BHSDCodec to use for decoding
* @param count
* the number of references to decode
* @param reference
* the array of values to use for the references
*
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* Codec
*/
public String[] parseReferences(String name, InputStream in,
BHSDCodec codec, int count, String[] reference) throws IOException,
Pack200Exception {
return parseReferences(name, in, codec, new int[] { count }, reference)[0];
}
/**
* Helper method to parse <i>count</i> references from <code>in</code>,
* using <code>codec</code> to decode the values as indexes into
* <code>reference</code> (which is populated prior to this call). An
* exception is thrown if a decoded index falls outside the range
* [0..reference.length-1]. Unlike the other parseReferences, this
* post-processes the result into an array of results.
*
* @param name
* TODO
* @param in
* the input stream to read from
* @param codec
* the BHSDCodec to use for decoding
* @param counts
* the numbers of references to decode for each array entry
* @param reference
* the array of values to use for the references
*
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* Codec
*/
public String[][] parseReferences(String name, InputStream in,
BHSDCodec codec, int counts[], String[] reference)
throws IOException, Pack200Exception {
int count = counts.length;
if (count == 0) {
return new String[][] { {} };
}
String[][] result = new String[count][];
int sum = 0;
for (int i = 0; i < count; i++) {
result[i] = new String[counts[i]];
sum += counts[i];
}
// TODO Merge the decode and parsing of a multiple structure into one
String[] result1 = new String[sum];
int[] indices = decodeBandInt(name, in, codec, sum);
for (int i1 = 0; i1 < sum; i1++) {
int index = indices[i1];
if (index < 0 || index >= reference.length)
throw new Pack200Exception(
"Something has gone wrong during parsing references, index = "
+ index + ", array size = " + reference.length);
result1[i1] = reference[index];
}
String[] refs = result1;
// TODO Merge the decode and parsing of a multiple structure into one
int pos = 0;
for (int i = 0; i < count; i++) {
int num = counts[i];
result[i] = new String[num];
System.arraycopy(refs, pos, result[i], 0, num);
pos += num;
}
return result;
}
public CPInteger[] parseCPIntReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] reference = segment.getCpBands().getCpInt();
int[] indices = decodeBandInt(name, in, codec, count);
CPInteger[] result = new CPInteger[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
if (index < 0 || index >= reference.length)
throw new Pack200Exception(
"Something has gone wrong during parsing references, index = "
+ index + ", array size = " + reference.length);
result[i1] = segment.getCpBands().cpIntegerValue(index);
}
return result;
}
public CPDouble[] parseCPDoubleReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPDouble[] result = new CPDouble[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpDoubleValue(index);
}
return result;
}
public CPFloat[] parseCPFloatReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPFloat[] result = new CPFloat[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpFloatValue(index);
}
return result;
}
public CPLong[] parseCPLongReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
long[] reference = segment.getCpBands().getCpLong();
int[] indices = decodeBandInt(name, in, codec, count);
CPLong[] result = new CPLong[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
if (index < 0 || index >= reference.length)
throw new Pack200Exception(
"Something has gone wrong during parsing references, index = "
+ index + ", array size = " + reference.length);
result[i1] = segment.getCpBands().cpLongValue(index);
}
return result;
}
public CPUTF8[] parseCPUTF8References(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPUTF8[] result = new CPUTF8[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpUTF8Value(index);
}
return result;
}
public CPUTF8[][] parseCPUTF8References(String name, InputStream in,
BHSDCodec codec, int[] counts) throws IOException, Pack200Exception {
CPUTF8[][] result = new CPUTF8[counts.length][];
int sum = 0;
for (int i = 0; i < counts.length; i++) {
result[i] = new CPUTF8[counts[i]];
sum += counts[i];
}
CPUTF8[] result1 = new CPUTF8[sum];
int[] indices = decodeBandInt(name, in, codec, sum);
for (int i1 = 0; i1 < sum; i1++) {
int index = indices[i1];
result1[i1] = segment.getCpBands().cpUTF8Value(index);
}
CPUTF8[] refs = result1;
int pos = 0;
for (int i = 0; i < counts.length; i++) {
int num = counts[i];
result[i] = new CPUTF8[num];
System.arraycopy(refs, pos, result[i], 0, num);
pos += num;
}
return result;
}
public CPString[] parseCPStringReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPString[] result = new CPString[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpStringValue(index);
}
return result;
}
public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(
String name, InputStream in, BHSDCodec codec, int count)
throws IOException, Pack200Exception {
CpBands cpBands = segment.getCpBands();
int[] indices = decodeBandInt(name, in, codec, count);
CPInterfaceMethodRef[] result = new CPInterfaceMethodRef[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = cpBands.cpIMethodValue(index);
}
return result;
}
public CPMethodRef[] parseCPMethodRefReferences(String name,
InputStream in, BHSDCodec codec, int count) throws IOException,
Pack200Exception {
CpBands cpBands = segment.getCpBands();
int[] indices = decodeBandInt(name, in, codec, count);
CPMethodRef[] result = new CPMethodRef[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = cpBands.cpMethodValue(index);
}
return result;
}
public CPFieldRef[] parseCPFieldRefReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
CpBands cpBands = segment.getCpBands();
int[] indices = decodeBandInt(name, in, codec, count);
CPFieldRef[] result = new CPFieldRef[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = cpBands.cpFieldValue(index);
}
return result;
}
public CPNameAndType[] parseCPDescriptorReferences(String name,
InputStream in, BHSDCodec codec, int count) throws IOException,
Pack200Exception {
CpBands cpBands = segment.getCpBands();
int[] indices = decodeBandInt(name, in, codec, count);
CPNameAndType[] result = new CPNameAndType[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = cpBands.cpNameAndTypeValue(index);
}
return result;
}
public CPUTF8[] parseCPSignatureReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPUTF8[] result = new CPUTF8[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpSignatureValue(index);
}
return result;
}
protected CPUTF8[][] parseCPSignatureReferences(String name, InputStream in,
BHSDCodec codec, int[] counts) throws IOException, Pack200Exception {
CPUTF8[][] result = new CPUTF8[counts.length][];
int sum = 0;
for (int i = 0; i < counts.length; i++) {
result[i] = new CPUTF8[counts[i]];
sum += counts[i];
}
CPUTF8[] result1 = new CPUTF8[sum];
int[] indices = decodeBandInt(name, in, codec, sum);
for (int i1 = 0; i1 < sum; i1++) {
int index = indices[i1];
result1[i1] = segment.getCpBands().cpSignatureValue(index);
}
CPUTF8[] refs = result1;
int pos = 0;
for (int i = 0; i < counts.length; i++) {
int num = counts[i];
result[i] = new CPUTF8[num];
System.arraycopy(refs, pos, result[i], 0, num);
pos += num;
}
return result;
}
public CPClass[] parseCPClassReferences(String name, InputStream in,
BHSDCodec codec, int count) throws IOException, Pack200Exception {
int[] indices = decodeBandInt(name, in, codec, count);
CPClass[] result = new CPClass[indices.length];
for (int i1 = 0; i1 < count; i1++) {
int index = indices[i1];
result[i1] = segment.getCpBands().cpClassValue(index);
}
return result;
}
protected String[] getReferences(int[] ints, String[] reference) {
String[] result = new String[ints.length];
for (int i = 0; i < result.length; i++) {
result[i] = reference[ints[i]];
}
return result;
}
protected String[][] getReferences(int[][] ints, String[] reference) {
String[][] result = new String[ints.length][];
for (int i = 0; i < result.length; i++) {
result[i] = new String[ints[i].length];
for (int j = 0; j < result[i].length; j++) {
result[i][j] = reference[ints[i][j]];
}
}
return result;
}
}

@ -0,0 +1,606 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.Attribute;
import org.apache.harmony.unpack200.bytecode.BCIRenumberedAttribute;
import org.apache.harmony.unpack200.bytecode.ByteCode;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.CodeAttribute;
import org.apache.harmony.unpack200.bytecode.ExceptionTableEntry;
import org.apache.harmony.unpack200.bytecode.NewAttribute;
import org.apache.harmony.unpack200.bytecode.OperandManager;
/**
* Bytecode bands
*/
public class BcBands extends BandSet {
// The bytecodes for each method in each class as they come (i.e. in their
// packed format)
private byte[][][] methodByteCodePacked;
// The bands
// TODO: Haven't resolved references yet. Do we want to?
private int[] bcCaseCount;
private int[] bcCaseValue;
private int[] bcByte;
private int[] bcLocal;
private int[] bcShort;
private int[] bcLabel;
private int[] bcIntRef;
private int[] bcFloatRef;
private int[] bcLongRef;
private int[] bcDoubleRef;
private int[] bcStringRef;
private int[] bcClassRef;
private int[] bcFieldRef;
private int[] bcMethodRef;
private int[] bcIMethodRef;
private int[] bcThisField;
private int[] bcSuperField;
private int[] bcThisMethod;
private int[] bcSuperMethod;
private int[] bcInitRef;
private int[] bcEscRef;
private int[] bcEscRefSize;
private int[] bcEscSize;
private int[][] bcEscByte;
private List wideByteCodes;
/**
* @param segment
*/
public BcBands(Segment segment) {
super(segment);
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
public void read(InputStream in) throws IOException, Pack200Exception {
AttributeLayoutMap attributeDefinitionMap = segment
.getAttrDefinitionBands().getAttributeDefinitionMap();
int classCount = header.getClassCount();
long[][] methodFlags = segment.getClassBands().getMethodFlags();
int bcCaseCountCount = 0;
int bcByteCount = 0;
int bcShortCount = 0;
int bcLocalCount = 0;
int bcLabelCount = 0;
int bcIntRefCount = 0;
int bcFloatRefCount = 0;
int bcLongRefCount = 0;
int bcDoubleRefCount = 0;
int bcStringRefCount = 0;
int bcClassRefCount = 0;
int bcFieldRefCount = 0;
int bcMethodRefCount = 0;
int bcIMethodRefCount = 0;
int bcThisFieldCount = 0;
int bcSuperFieldCount = 0;
int bcThisMethodCount = 0;
int bcSuperMethodCount = 0;
int bcInitRefCount = 0;
int bcEscCount = 0;
int bcEscRefCount = 0;
AttributeLayout abstractModifier = attributeDefinitionMap
.getAttributeLayout(AttributeLayout.ACC_ABSTRACT,
AttributeLayout.CONTEXT_METHOD);
AttributeLayout nativeModifier = attributeDefinitionMap
.getAttributeLayout(AttributeLayout.ACC_NATIVE,
AttributeLayout.CONTEXT_METHOD);
methodByteCodePacked = new byte[classCount][][];
int bcParsed = 0;
List switchIsTableSwitch = new ArrayList();
wideByteCodes = new ArrayList();
for (int c = 0; c < classCount; c++) {
int numberOfMethods = methodFlags[c].length;
methodByteCodePacked[c] = new byte[numberOfMethods][];
for (int m = 0; m < numberOfMethods; m++) {
long methodFlag = methodFlags[c][m];
if (!abstractModifier.matches(methodFlag)
&& !nativeModifier.matches(methodFlag)) {
ByteArrayOutputStream codeBytes = new ByteArrayOutputStream();
byte code;
while ((code = (byte) (0xff & in.read())) != -1)
codeBytes.write(code);
methodByteCodePacked[c][m] = codeBytes.toByteArray();
bcParsed += methodByteCodePacked[c][m].length;
int[] codes = new int[methodByteCodePacked[c][m].length];
for (int i = 0; i < codes.length; i++) {
codes[i] = methodByteCodePacked[c][m][i] & 0xff;
}
for (int i = 0; i < methodByteCodePacked[c][m].length; i++) {
int codePacked = 0xff & methodByteCodePacked[c][m][i];
switch (codePacked) {
case 16: // bipush
case 188: // newarray
bcByteCount++;
break;
case 17: // sipush
bcShortCount++;
break;
case 18: // (a)ldc
case 19: // aldc_w
bcStringRefCount++;
break;
case 234: // ildc
case 237: // ildc_w
bcIntRefCount++;
break;
case 235: // fldc
case 238: // fldc_w
bcFloatRefCount++;
break;
case 197: // multianewarray
bcByteCount++;
// fallthrough intended
case 233: // cldc
case 236: // cldc_w
case 187: // new
case 189: // anewarray
case 192: // checkcast
case 193: // instanceof
bcClassRefCount++;
break;
case 20: // lldc2_w
bcLongRefCount++;
break;
case 239: // dldc2_w
bcDoubleRefCount++;
break;
case 169: // ret
bcLocalCount++;
break;
case 167: // goto
case 168: // jsr
case 200: // goto_w
case 201: // jsr_w
bcLabelCount++;
break;
case 170: // tableswitch
switchIsTableSwitch.add(new Boolean(true));
bcCaseCountCount++;
bcLabelCount++;
break;
case 171: // lookupswitch
switchIsTableSwitch.add(new Boolean(false));
bcCaseCountCount++;
bcLabelCount++;
break;
case 178: // getstatic
case 179: // putstatic
case 180: // getfield
case 181: // putfield
bcFieldRefCount++;
break;
case 182: // invokevirtual
case 183: // invokespecial
case 184: // invokestatic
bcMethodRefCount++;
break;
case 185: // invokeinterface
bcIMethodRefCount++;
break;
case 202: // getstatic_this
case 203: // putstatic_this
case 204: // getfield_this
case 205: // putfield_this
case 209: // aload_0_getstatic_this
case 210: // aload_0_putstatic_this
case 211: // aload_0_putfield_this
case 212: // aload_0_putfield_this
bcThisFieldCount++;
break;
case 206: // invokevirtual_this
case 207: // invokespecial_this
case 208: // invokestatic_this
case 213: // aload_0_invokevirtual_this
case 214: // aload_0_invokespecial_this
case 215: // aload_0_invokestatic_this
bcThisMethodCount++;
break;
case 216: // getstatic_super
case 217: // putstatic_super
case 218: // getfield_super
case 219: // putfield_super
case 223: // aload_0_getstatic_super
case 224: // aload_0_putstatic_super
case 225: // aload_0_getfield_super
case 226: // aload_0_putfield_super
bcSuperFieldCount++;
break;
case 220: // invokevirtual_super
case 221: // invokespecial_super
case 222: // invokestatic_super
case 227: // aload_0_invokevirtual_super
case 228: // aload_0_invokespecial_super
case 229: // aload_0_invokestatic_super
bcSuperMethodCount++;
break;
case 132: // iinc
bcLocalCount++;
bcByteCount++;
break;
case 196: // wide
int nextInstruction = 0xff & methodByteCodePacked[c][m][i + 1];
wideByteCodes.add(new Integer(nextInstruction));
if (nextInstruction == 132) { // iinc
bcLocalCount++;
bcShortCount++;
} else if (endsWithLoad(nextInstruction)
|| endsWithStore(nextInstruction)
|| nextInstruction == 169) {
bcLocalCount++;
} else {
segment
.log(
Segment.LOG_LEVEL_VERBOSE,
"Found unhandled "
+ ByteCode
.getByteCode(nextInstruction));
}
i++;
break;
case 230: // invokespecial_this_init
case 231: // invokespecial_super_init
case 232: // invokespecial_new_init
bcInitRefCount++;
break;
case 253: // ref_escape
bcEscRefCount++;
break;
case 254: // byte_escape
bcEscCount++;
break;
default:
if (endsWithLoad(codePacked)
|| endsWithStore(codePacked)) {
bcLocalCount++;
} else if (startsWithIf(codePacked)) {
bcLabelCount++;
}
}
}
}
}
}
// other bytecode bands
bcCaseCount = decodeBandInt("bc_case_count", in, Codec.UNSIGNED5,
bcCaseCountCount);
int bcCaseValueCount = 0;
for (int i = 0; i < bcCaseCount.length; i++) {
boolean isTableSwitch = ((Boolean) switchIsTableSwitch.get(i))
.booleanValue();
if (isTableSwitch) {
bcCaseValueCount += 1;
} else {
bcCaseValueCount += bcCaseCount[i];
}
}
bcCaseValue = decodeBandInt("bc_case_value", in, Codec.DELTA5,
bcCaseValueCount);
// Every case value needs a label. We weren't able to count these
// above, because we didn't know how many cases there were.
// Have to correct it now.
for (int index = 0; index < bcCaseCountCount; index++) {
bcLabelCount += bcCaseCount[index];
}
bcByte = decodeBandInt("bc_byte", in, Codec.BYTE1, bcByteCount);
bcShort = decodeBandInt("bc_short", in, Codec.DELTA5, bcShortCount);
bcLocal = decodeBandInt("bc_local", in, Codec.UNSIGNED5, bcLocalCount);
bcLabel = decodeBandInt("bc_label", in, Codec.BRANCH5, bcLabelCount);
bcIntRef = decodeBandInt("bc_intref", in, Codec.DELTA5, bcIntRefCount);
bcFloatRef = decodeBandInt("bc_floatref", in, Codec.DELTA5,
bcFloatRefCount);
bcLongRef = decodeBandInt("bc_longref", in, Codec.DELTA5,
bcLongRefCount);
bcDoubleRef = decodeBandInt("bc_doubleref", in, Codec.DELTA5,
bcDoubleRefCount);
bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5,
bcStringRefCount);
bcClassRef = decodeBandInt("bc_classref", in, Codec.UNSIGNED5,
bcClassRefCount);
bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5,
bcFieldRefCount);
bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5,
bcMethodRefCount);
bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5,
bcIMethodRefCount);
bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5,
bcThisFieldCount);
bcSuperField = decodeBandInt("bc_superfield", in, Codec.UNSIGNED5,
bcSuperFieldCount);
bcThisMethod = decodeBandInt("bc_thismethod", in, Codec.UNSIGNED5,
bcThisMethodCount);
bcSuperMethod = decodeBandInt("bc_supermethod", in, Codec.UNSIGNED5,
bcSuperMethodCount);
bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5,
bcInitRefCount);
bcEscRef = decodeBandInt("bc_escref", in, Codec.UNSIGNED5,
bcEscRefCount);
bcEscRefSize = decodeBandInt("bc_escrefsize", in, Codec.UNSIGNED5,
bcEscRefCount);
bcEscSize = decodeBandInt("bc_escsize", in, Codec.UNSIGNED5, bcEscCount);
bcEscByte = decodeBandInt("bc_escbyte", in, Codec.BYTE1, bcEscSize);
}
public void unpack() throws Pack200Exception {
int classCount = header.getClassCount();
long[][] methodFlags = segment.getClassBands().getMethodFlags();
int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals();
int[] codeMaxStack = segment.getClassBands().getCodeMaxStack();
ArrayList[][] methodAttributes = segment.getClassBands()
.getMethodAttributes();
String[][] methodDescr = segment.getClassBands().getMethodDescr();
AttributeLayoutMap attributeDefinitionMap = segment
.getAttrDefinitionBands().getAttributeDefinitionMap();
AttributeLayout abstractModifier = attributeDefinitionMap
.getAttributeLayout(AttributeLayout.ACC_ABSTRACT,
AttributeLayout.CONTEXT_METHOD);
AttributeLayout nativeModifier = attributeDefinitionMap
.getAttributeLayout(AttributeLayout.ACC_NATIVE,
AttributeLayout.CONTEXT_METHOD);
AttributeLayout staticModifier = attributeDefinitionMap
.getAttributeLayout(AttributeLayout.ACC_STATIC,
AttributeLayout.CONTEXT_METHOD);
int[] wideByteCodeArray = new int[wideByteCodes.size()];
for (int index = 0; index < wideByteCodeArray.length; index++) {
wideByteCodeArray[index] = ((Integer) wideByteCodes.get(index))
.intValue();
}
OperandManager operandManager = new OperandManager(bcCaseCount,
bcCaseValue, bcByte, bcShort, bcLocal, bcLabel, bcIntRef,
bcFloatRef, bcLongRef, bcDoubleRef, bcStringRef, bcClassRef,
bcFieldRef, bcMethodRef, bcIMethodRef, bcThisField,
bcSuperField, bcThisMethod, bcSuperMethod, bcInitRef,
wideByteCodeArray);
operandManager.setSegment(segment);
int i = 0;
ArrayList orderedCodeAttributes = segment.getClassBands()
.getOrderedCodeAttributes();
int codeAttributeIndex = 0;
// Exception table fields
int[] handlerCount = segment.getClassBands().getCodeHandlerCount();
int[][] handlerStartPCs = segment.getClassBands()
.getCodeHandlerStartP();
int[][] handlerEndPCs = segment.getClassBands().getCodeHandlerEndPO();
int[][] handlerCatchPCs = segment.getClassBands()
.getCodeHandlerCatchPO();
int[][] handlerClassTypes = segment.getClassBands()
.getCodeHandlerClassRCN();
boolean allCodeHasFlags = segment.getSegmentHeader().getOptions().hasAllCodeFlags();
boolean[] codeHasFlags = segment.getClassBands().getCodeHasAttributes();
for (int c = 0; c < classCount; c++) {
int numberOfMethods = methodFlags[c].length;
for (int m = 0; m < numberOfMethods; m++) {
long methodFlag = methodFlags[c][m];
if (!abstractModifier.matches(methodFlag)
&& !nativeModifier.matches(methodFlag)) {
int maxStack = codeMaxStack[i];
int maxLocal = codeMaxNALocals[i];
if (!staticModifier.matches(methodFlag))
maxLocal++; // one for 'this' parameter
// I believe this has to take wide arguments into account
maxLocal += SegmentUtils
.countInvokeInterfaceArgs(methodDescr[c][m]);
String[] cpClass = segment.getCpBands().getCpClass();
operandManager.setCurrentClass(cpClass[segment
.getClassBands().getClassThisInts()[c]]);
operandManager.setSuperClass(cpClass[segment
.getClassBands().getClassSuperInts()[c]]);
List exceptionTable = new ArrayList();
if (handlerCount != null) {
for (int j = 0; j < handlerCount[i]; j++) {
int handlerClass = handlerClassTypes[i][j] - 1;
CPClass cpHandlerClass = null;
if (handlerClass != -1) {
// The handlerClass will be null if the
// catch is a finally (that is, the
// exception table catch_type should be 0
cpHandlerClass = segment.getCpBands()
.cpClassValue(handlerClass);
}
ExceptionTableEntry entry = new ExceptionTableEntry(
handlerStartPCs[i][j], handlerEndPCs[i][j],
handlerCatchPCs[i][j], cpHandlerClass);
exceptionTable.add(entry);
}
}
CodeAttribute codeAttr = new CodeAttribute(maxStack,
maxLocal, methodByteCodePacked[c][m], segment,
operandManager, exceptionTable);
ArrayList methodAttributesList = methodAttributes[c][m];
// Make sure we add the code attribute in the right place
int indexForCodeAttr = 0;
for (int index = 0; index < methodAttributesList.size(); index++) {
Attribute attribute = (Attribute) methodAttributesList.get(index);
if((attribute instanceof NewAttribute && ((NewAttribute)attribute).getLayoutIndex() < 15)) {
indexForCodeAttr ++;
} else {
break;
}
}
methodAttributesList.add(indexForCodeAttr, codeAttr);
codeAttr.renumber(codeAttr.byteCodeOffsets);
List currentAttributes;
if (allCodeHasFlags) {
currentAttributes = (List) orderedCodeAttributes.get(i);
} else {
if (codeHasFlags[i]) {
currentAttributes = (List) orderedCodeAttributes
.get(codeAttributeIndex);
codeAttributeIndex++;
} else {
currentAttributes = Collections.EMPTY_LIST;
}
}
for (int index = 0; index < currentAttributes.size(); index++) {
Attribute currentAttribute = (Attribute) currentAttributes
.get(index);
codeAttr.addAttribute(currentAttribute);
// Fix up the line numbers if needed
if (currentAttribute.hasBCIRenumbering()) {
((BCIRenumberedAttribute) currentAttribute)
.renumber(codeAttr.byteCodeOffsets);
}
}
i++;
}
}
}
}
private boolean startsWithIf(int codePacked) {
return (codePacked >= 153 && codePacked <= 166) || (codePacked == 198)
|| (codePacked == 199);
}
private boolean endsWithLoad(int codePacked) {
return (codePacked >= 21 && codePacked <= 25);
}
private boolean endsWithStore(int codePacked) {
return (codePacked >= 54 && codePacked <= 58);
}
public byte[][][] getMethodByteCodePacked() {
return methodByteCodePacked;
}
public int[] getBcCaseCount() {
return bcCaseCount;
}
public int[] getBcCaseValue() {
return bcCaseValue;
}
public int[] getBcByte() {
return bcByte;
}
public int[] getBcClassRef() {
return bcClassRef;
}
public int[] getBcDoubleRef() {
return bcDoubleRef;
}
public int[] getBcFieldRef() {
return bcFieldRef;
}
public int[] getBcFloatRef() {
return bcFloatRef;
}
public int[] getBcIMethodRef() {
return bcIMethodRef;
}
public int[] getBcInitRef() {
return bcInitRef;
}
public int[] getBcIntRef() {
return bcIntRef;
}
public int[] getBcLabel() {
return bcLabel;
}
public int[] getBcLocal() {
return bcLocal;
}
public int[] getBcLongRef() {
return bcLongRef;
}
public int[] getBcMethodRef() {
return bcMethodRef;
}
public int[] getBcShort() {
return bcShort;
}
public int[] getBcStringRef() {
return bcStringRef;
}
public int[] getBcSuperField() {
return bcSuperField;
}
public int[] getBcSuperMethod() {
return bcSuperMethod;
}
public int[] getBcThisField() {
return bcThisField;
}
public int[] getBcThisMethod() {
return bcThisMethod;
}
public int[] getBcEscRef() {
return bcEscRef;
}
public int[] getBcEscRefSize() {
return bcEscRefSize;
}
public int[] getBcEscSize() {
return bcEscSize;
}
public int[][] getBcEscByte() {
return bcEscByte;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,724 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.CPDouble;
import org.apache.harmony.unpack200.bytecode.CPFieldRef;
import org.apache.harmony.unpack200.bytecode.CPFloat;
import org.apache.harmony.unpack200.bytecode.CPInteger;
import org.apache.harmony.unpack200.bytecode.CPInterfaceMethodRef;
import org.apache.harmony.unpack200.bytecode.CPLong;
import org.apache.harmony.unpack200.bytecode.CPMethodRef;
import org.apache.harmony.unpack200.bytecode.CPNameAndType;
import org.apache.harmony.unpack200.bytecode.CPString;
import org.apache.harmony.unpack200.bytecode.CPUTF8;
/**
* Constant Pool bands
*/
public class CpBands extends BandSet {
public SegmentConstantPool getConstantPool() {
return pool;
}
private final SegmentConstantPool pool = new SegmentConstantPool(this);
private String[] cpClass;
private int[] cpClassInts;
private int[] cpDescriptorNameInts;
private int[] cpDescriptorTypeInts;
private String[] cpDescriptor;
private double[] cpDouble;
private String[] cpFieldClass;
private String[] cpFieldDescriptor;
private int[] cpFieldClassInts;
private int[] cpFieldDescriptorInts;
private float[] cpFloat;
private String[] cpIMethodClass;
private String[] cpIMethodDescriptor;
private int[] cpIMethodClassInts;
private int[] cpIMethodDescriptorInts;
private int[] cpInt;
private long[] cpLong;
private String[] cpMethodClass;
private String[] cpMethodDescriptor;
private int[] cpMethodClassInts;
private int[] cpMethodDescriptorInts;
private String[] cpSignature;
private int[] cpSignatureInts;
private String[] cpString;
private int[] cpStringInts;
private String[] cpUTF8;
private final Map stringsToCPUTF8 = new HashMap();
private final Map stringsToCPStrings = new HashMap();
private final Map longsToCPLongs = new HashMap();
private final Map integersToCPIntegers = new HashMap();
private final Map floatsToCPFloats = new HashMap();
private final Map stringsToCPClass = new HashMap();
private final Map doublesToCPDoubles = new HashMap();
private final Map descriptorsToCPNameAndTypes = new HashMap();
private Map mapClass;
private Map mapDescriptor;
private Map mapUTF8;
// TODO: Not used
// private Map mapSignature;
private int intOffset;
private int floatOffset;
private int longOffset;
private int doubleOffset;
private int stringOffset;
private int classOffset;
private int signatureOffset;
private int descrOffset;
private int fieldOffset;
private int methodOffset;
private int imethodOffset;
public CpBands(Segment segment) {
super(segment);
}
public void read(InputStream in) throws IOException, Pack200Exception {
parseCpUtf8(in);
parseCpInt(in);
parseCpFloat(in);
parseCpLong(in);
parseCpDouble(in);
parseCpString(in);
parseCpClass(in);
parseCpSignature(in);
parseCpDescriptor(in);
parseCpField(in);
parseCpMethod(in);
parseCpIMethod(in);
intOffset = cpUTF8.length;
floatOffset = intOffset + cpInt.length;
longOffset = floatOffset + cpFloat.length;
doubleOffset = longOffset + cpLong.length;
stringOffset = doubleOffset + cpDouble.length;
classOffset = stringOffset + cpString.length;
signatureOffset = classOffset + cpClass.length;
descrOffset = signatureOffset + cpSignature.length;
fieldOffset = descrOffset + cpDescriptor.length;
methodOffset = fieldOffset + cpFieldClass.length;
imethodOffset = methodOffset + cpMethodClass.length;
}
public void unpack() {
}
/**
* Parses the constant pool class names, using {@link #cpClassCount} to
* populate {@link #cpClass} from {@link #cpUTF8}.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpClass(InputStream in) throws IOException,
Pack200Exception {
int cpClassCount = header.getCpClassCount();
cpClassInts = decodeBandInt("cp_Class", in, Codec.UDELTA5, cpClassCount);
cpClass = new String[cpClassCount];
mapClass = new HashMap(cpClassCount);
for (int i = 0; i < cpClassCount; i++) {
cpClass[i] = cpUTF8[cpClassInts[i]];
mapClass.put(cpClass[i], new Integer(i));
}
}
/**
* Parses the constant pool descriptor definitions, using
* {@link #cpDescriptorCount} to populate {@link #cpDescriptor}. For ease
* of use, the cpDescriptor is stored as a string of the form <i>name:type</i>,
* largely to make it easier for representing field and method descriptors
* (e.g. <code>out:java.lang.PrintStream</code>) in a way that is
* compatible with passing String arrays.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpDescriptor(InputStream in) throws IOException,
Pack200Exception {
int cpDescriptorCount = header.getCpDescriptorCount();
cpDescriptorNameInts = decodeBandInt("cp_Descr_name", in, Codec.DELTA5,
cpDescriptorCount);
cpDescriptorTypeInts = decodeBandInt("cp_Descr_type", in,
Codec.UDELTA5, cpDescriptorCount);
String[] cpDescriptorNames = getReferences(cpDescriptorNameInts, cpUTF8);
String[] cpDescriptorTypes = getReferences(cpDescriptorTypeInts,
cpSignature);
cpDescriptor = new String[cpDescriptorCount];
mapDescriptor = new HashMap(cpDescriptorCount);
for (int i = 0; i < cpDescriptorCount; i++) {
cpDescriptor[i] = cpDescriptorNames[i] + ":" + cpDescriptorTypes[i]; //$NON-NLS-1$
mapDescriptor.put(cpDescriptor[i], new Integer(i));
}
}
private void parseCpDouble(InputStream in) throws IOException,
Pack200Exception {
int cpDoubleCount = header.getCpDoubleCount();
long[] band = parseFlags("cp_Double", in, cpDoubleCount, Codec.UDELTA5,
Codec.DELTA5);
cpDouble = new double[band.length];
for (int i = 0; i < band.length; i++) {
cpDouble[i] = Double.longBitsToDouble(band[i]);
}
}
/**
* Parses the constant pool field definitions, using {@link #cpFieldCount}
* to populate {@link #cpFieldClass} and {@link #cpFieldDescriptor}.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpField(InputStream in) throws IOException,
Pack200Exception {
int cpFieldCount = header.getCpFieldCount();
cpFieldClassInts = decodeBandInt("cp_Field_class", in, Codec.DELTA5,
cpFieldCount);
cpFieldDescriptorInts = decodeBandInt("cp_Field_desc", in, Codec.UDELTA5,
cpFieldCount);
cpFieldClass = new String[cpFieldCount];
cpFieldDescriptor = new String[cpFieldCount];
for (int i = 0; i < cpFieldCount; i++) {
cpFieldClass[i] = cpClass[cpFieldClassInts[i]];
cpFieldDescriptor[i] = cpDescriptor[cpFieldDescriptorInts[i]];
}
}
private void parseCpFloat(InputStream in) throws IOException,
Pack200Exception {
int cpFloatCount = header.getCpFloatCount();
cpFloat = new float[cpFloatCount];
int floatBits[] = decodeBandInt("cp_Float", in, Codec.UDELTA5,
cpFloatCount);
for (int i = 0; i < cpFloatCount; i++) {
cpFloat[i] = Float.intBitsToFloat(floatBits[i]);
}
}
/**
* Parses the constant pool interface method definitions, using
* {@link #cpIMethodCount} to populate {@link #cpIMethodClass} and
* {@link #cpIMethodDescriptor}.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpIMethod(InputStream in) throws IOException,
Pack200Exception {
int cpIMethodCount = header.getCpIMethodCount();
cpIMethodClassInts = decodeBandInt("cp_Imethod_class", in, Codec.DELTA5,
cpIMethodCount);
cpIMethodDescriptorInts = decodeBandInt("cp_Imethod_desc", in,
Codec.UDELTA5, cpIMethodCount);
cpIMethodClass = new String[cpIMethodCount];
cpIMethodDescriptor = new String[cpIMethodCount];
for (int i = 0; i < cpIMethodCount; i++) {
cpIMethodClass[i] = cpClass[cpIMethodClassInts[i]];
cpIMethodDescriptor[i] = cpDescriptor[cpIMethodDescriptorInts[i]];
}
}
private void parseCpInt(InputStream in) throws IOException,
Pack200Exception {
int cpIntCount = header.getCpIntCount();
cpInt = decodeBandInt("cpInt", in, Codec.UDELTA5, cpIntCount);
}
private void parseCpLong(InputStream in) throws IOException,
Pack200Exception {
int cpLongCount = header.getCpLongCount();
cpLong = parseFlags("cp_Long", in, cpLongCount, Codec.UDELTA5,
Codec.DELTA5);
}
/**
* Parses the constant pool method definitions, using {@link #cpMethodCount}
* to populate {@link #cpMethodClass} and {@link #cpMethodDescriptor}.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpMethod(InputStream in) throws IOException,
Pack200Exception {
int cpMethodCount = header.getCpMethodCount();
cpMethodClassInts = decodeBandInt("cp_Method_class", in, Codec.DELTA5,
cpMethodCount);
cpMethodDescriptorInts = decodeBandInt("cp_Method_desc", in,
Codec.UDELTA5, cpMethodCount);
cpMethodClass = new String[cpMethodCount];
cpMethodDescriptor = new String[cpMethodCount];
for (int i = 0; i < cpMethodCount; i++) {
cpMethodClass[i] = cpClass[cpMethodClassInts[i]];
cpMethodDescriptor[i] = cpDescriptor[cpMethodDescriptorInts[i]];
}
}
/**
* Parses the constant pool signature classes, using
* {@link #cpSignatureCount} to populate {@link #cpSignature}. A signature
* form is akin to the bytecode representation of a class; Z for boolean, I
* for int, [ for array etc. However, although classes are started with L,
* the classname does not follow the form; instead, there is a separate
* array of classes. So an array corresponding to
* <code>public static void main(String args[])</code> has a form of
* <code>[L(V)</code> and a classes array of
* <code>[java.lang.String]</code>. The {@link #cpSignature} is a string
* representation identical to the bytecode equivalent
* <code>[Ljava/lang/String;(V)</code> TODO Check that the form is as
* above and update other types e.g. J
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpSignature(InputStream in) throws IOException,
Pack200Exception {
int cpSignatureCount = header.getCpSignatureCount();
cpSignatureInts = decodeBandInt("cp_Signature_form", in,
Codec.DELTA5, cpSignatureCount);
String[] cpSignatureForm = getReferences(cpSignatureInts, cpUTF8);
cpSignature = new String[cpSignatureCount];
int lCount = 0;
for (int i = 0; i < cpSignatureCount; i++) {
String form = cpSignatureForm[i];
char[] chars = form.toCharArray();
for (int j = 0; j < chars.length; j++) {
if (chars[j] == 'L') {
cpSignatureInts[i] = -1;
lCount++;
}
}
}
String[] cpSignatureClasses = parseReferences("cp_Signature_classes",
in, Codec.UDELTA5, lCount, cpClass);
int index = 0;
for (int i = 0; i < cpSignatureCount; i++) {
String form = cpSignatureForm[i];
int len = form.length();
StringBuffer signature = new StringBuffer(64);
ArrayList list = new ArrayList();
for (int j = 0; j < len; j++) {
char c = form.charAt(j);
signature.append(c);
if (c == 'L') {
String className = cpSignatureClasses[index];
list.add(className);
signature.append(className);
index++;
}
}
cpSignature[i] = signature.toString();
}
// for (int i = 0; i < cpSignatureInts.length; i++) {
// if(cpSignatureInts[i] == -1) {
// cpSignatureInts[i] = search(cpUTF8, cpSignature[i]);
// }
// }
}
/**
* Parses the constant pool strings, using {@link #cpStringCount} to
* populate {@link #cpString} from indexes into {@link #cpUTF8}.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseCpString(InputStream in) throws IOException,
Pack200Exception {
int cpStringCount = header.getCpStringCount();
cpStringInts = decodeBandInt("cp_String", in, Codec.UDELTA5,
cpStringCount);
cpString = new String[cpStringCount];
for (int i = 0; i < cpStringCount; i++) {
cpString[i] = cpUTF8[cpStringInts[i]];
}
}
private void parseCpUtf8(InputStream in) throws IOException,
Pack200Exception {
int cpUTF8Count = header.getCpUTF8Count();
cpUTF8 = new String[cpUTF8Count];
mapUTF8 = new HashMap(cpUTF8Count+1);
cpUTF8[0] = ""; //$NON-NLS-1$
mapUTF8.put("", new Integer(0));
int[] prefix = decodeBandInt("cpUTF8Prefix", in, Codec.DELTA5,
cpUTF8Count - 2);
int charCount = 0;
int bigSuffixCount = 0;
int[] suffix = decodeBandInt("cpUTF8Suffix", in, Codec.UNSIGNED5,
cpUTF8Count - 1);
for (int i = 0; i < suffix.length; i++) {
if (suffix[i] == 0) {
bigSuffixCount++;
} else {
charCount += suffix[i];
}
}
char[] data = new char[charCount];
int[] dataBand = decodeBandInt("cp_Utf8_chars", in, Codec.CHAR3,
charCount);
for (int i = 0; i < data.length; i++) {
data[i] = (char) dataBand[i];
}
// Read in the big suffix data
int[] bigSuffixCounts = decodeBandInt("cp_Utf8_big_suffix", in,
Codec.DELTA5, bigSuffixCount);
int[][] bigSuffixDataBand = new int[bigSuffixCount][];
for (int i = 0; i < bigSuffixDataBand.length; i++) {
bigSuffixDataBand[i] = decodeBandInt("cp_Utf8_big_chars " + i, in,
Codec.DELTA5, bigSuffixCounts[i]);
}
// Convert big suffix data to characters
char bigSuffixData[][] = new char[bigSuffixCount][];
for (int i = 0; i < bigSuffixDataBand.length; i++) {
bigSuffixData[i] = new char[bigSuffixDataBand[i].length];
for (int j = 0; j < bigSuffixDataBand[i].length; j++) {
bigSuffixData[i][j] = (char) bigSuffixDataBand[i][j];
}
}
// Go through the strings
charCount = 0;
bigSuffixCount = 0;
for (int i = 1; i < cpUTF8Count; i++) {
String lastString = cpUTF8[i - 1];
if (suffix[i - 1] == 0) {
// The big suffix stuff hasn't been tested, and I'll be
// surprised if it works first time w/o errors ...
cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0)
+ new String(bigSuffixData[bigSuffixCount++]);
mapUTF8.put(cpUTF8[i], new Integer(i));
} else {
cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0)
+ new String(data, charCount, suffix[i - 1]);
charCount += suffix[i - 1];
mapUTF8.put(cpUTF8[i], new Integer(i));
}
}
}
public String[] getCpClass() {
return cpClass;
}
public String[] getCpDescriptor() {
return cpDescriptor;
}
public double[] getCpDouble() {
return cpDouble;
}
public String[] getCpFieldClass() {
return cpFieldClass;
}
public String[] getCpFieldDescriptor() {
return cpFieldDescriptor;
}
public float[] getCpFloat() {
return cpFloat;
}
public String[] getCpIMethodClass() {
return cpIMethodClass;
}
public String[] getCpIMethodDescriptor() {
return cpIMethodDescriptor;
}
public int[] getCpInt() {
return cpInt;
}
public long[] getCpLong() {
return cpLong;
}
public String[] getCpMethodClass() {
return cpMethodClass;
}
public String[] getCpMethodDescriptor() {
return cpMethodDescriptor;
}
public String[] getCpSignature() {
return cpSignature;
}
public String[] getCpString() {
return cpString;
}
public String[] getCpUTF8() {
return cpUTF8;
}
public CPUTF8 cpUTF8Value(int index) {
String string = cpUTF8[index];
CPUTF8 cputf8 = (CPUTF8) stringsToCPUTF8.get(string);
if (cputf8 == null) {
cputf8 = new CPUTF8(string, index);
stringsToCPUTF8.put(string, cputf8);
} else if(cputf8.getGlobalIndex() > index) {
cputf8.setGlobalIndex(index);
}
return cputf8;
}
public CPUTF8 cpUTF8Value(String string) {
return cpUTF8Value(string, true);
}
public CPUTF8 cpUTF8Value(String string, boolean searchForIndex) {
CPUTF8 cputf8 = (CPUTF8) stringsToCPUTF8.get(string);
if (cputf8 == null) {
Integer index = null;
if(searchForIndex) {
index = (Integer)mapUTF8.get(string);
}
if(index != null) {
return cpUTF8Value(index.intValue());
}
/* TODO: mapSignature is not filled anywhere
if(searchForIndex) {
index = (Integer)mapSignature.get(string);
}
if(index != null) {
return cpSignatureValue(index.intValue());
}*/
cputf8 = new CPUTF8(string, -1);
stringsToCPUTF8.put(string, cputf8);
}
return cputf8;
}
public CPString cpStringValue(int index) {
String string = cpString[index];
int utf8Index = cpStringInts[index];
int globalIndex = stringOffset + index;
CPString cpString = (CPString) stringsToCPStrings.get(string);
if (cpString == null) {
cpString = new CPString(cpUTF8Value(utf8Index), globalIndex);
stringsToCPStrings.put(string, cpString);
}
return cpString;
}
public CPLong cpLongValue(int index) {
Long l = new Long(cpLong[index]);
CPLong cpLong = (CPLong) longsToCPLongs.get(l);
if (cpLong == null) {
cpLong = new CPLong(l, index + longOffset);
longsToCPLongs.put(l, cpLong);
}
return cpLong;
}
public CPInteger cpIntegerValue(int index) {
Integer i = new Integer(cpInt[index]);
CPInteger cpInteger = (CPInteger) integersToCPIntegers.get(i);
if (cpInteger == null) {
cpInteger = new CPInteger(i, index + intOffset);
integersToCPIntegers.put(i, cpInteger);
}
return cpInteger;
}
public CPFloat cpFloatValue(int index) {
Float f = new Float(cpFloat[index]);
CPFloat cpFloat = (CPFloat) floatsToCPFloats.get(f);
if (cpFloat == null) {
cpFloat = new CPFloat(f, index + floatOffset);
floatsToCPFloats.put(f, cpFloat);
}
return cpFloat;
}
public CPClass cpClassValue(int index) {
String string = cpClass[index];
int utf8Index = cpClassInts[index];
int globalIndex = classOffset + index;
CPClass cpString = (CPClass) stringsToCPClass.get(string);
if (cpString == null) {
cpString = new CPClass(cpUTF8Value(utf8Index), globalIndex);
stringsToCPClass.put(string, cpString);
}
return cpString;
}
public CPClass cpClassValue(String string) {
CPClass cpString = (CPClass) stringsToCPClass.get(string);
if (cpString == null) {
Integer index = (Integer)mapClass.get(string);
if(index != null) {
return cpClassValue(index.intValue());
}
cpString = new CPClass(cpUTF8Value(string, false), -1);
stringsToCPClass.put(string, cpString);
}
return cpString;
}
public CPDouble cpDoubleValue(int index) {
Double dbl = new Double(cpDouble[index]);
CPDouble cpDouble = (CPDouble) doublesToCPDoubles.get(dbl);
if (cpDouble == null) {
cpDouble = new CPDouble(dbl, index + doubleOffset);
doublesToCPDoubles.put(dbl, cpDouble);
}
return cpDouble;
}
public CPNameAndType cpNameAndTypeValue(int index) {
String descriptor = cpDescriptor[index];
CPNameAndType cpNameAndType = (CPNameAndType) descriptorsToCPNameAndTypes
.get(descriptor);
if (cpNameAndType == null) {
int nameIndex = cpDescriptorNameInts[index];
int descriptorIndex = cpDescriptorTypeInts[index];
CPUTF8 name = cpUTF8Value(nameIndex);
CPUTF8 descriptorU = cpSignatureValue(descriptorIndex);
cpNameAndType = new CPNameAndType(name, descriptorU, index + descrOffset);
descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
}
return cpNameAndType;
}
public CPInterfaceMethodRef cpIMethodValue(int index) {
return new CPInterfaceMethodRef(cpClassValue(cpIMethodClassInts[index]),
cpNameAndTypeValue(cpIMethodDescriptorInts[index]), index
+ imethodOffset);
}
public CPMethodRef cpMethodValue(int index) {
return new CPMethodRef(cpClassValue(cpMethodClassInts[index]),
cpNameAndTypeValue(cpMethodDescriptorInts[index]), index
+ methodOffset);
}
public CPFieldRef cpFieldValue(int index) {
return new CPFieldRef(cpClassValue(cpFieldClassInts[index]),
cpNameAndTypeValue(cpFieldDescriptorInts[index]), index
+ fieldOffset);
}
public CPUTF8 cpSignatureValue(int index) {
int globalIndex;
if(cpSignatureInts[index] != -1) {
globalIndex = cpSignatureInts[index];
} else {
globalIndex = index + signatureOffset;
}
String string = cpSignature[index];
CPUTF8 cpUTF8 = (CPUTF8) stringsToCPUTF8.get(string);
if(cpUTF8 == null) {
cpUTF8 = new CPUTF8(string, globalIndex);
stringsToCPUTF8.put(string, cpUTF8);
}
return cpUTF8;
}
public CPNameAndType cpNameAndTypeValue(String descriptor) {
CPNameAndType cpNameAndType = (CPNameAndType) descriptorsToCPNameAndTypes
.get(descriptor);
if (cpNameAndType == null) {
Integer index = (Integer)mapDescriptor.get(descriptor);
if(index != null) {
return cpNameAndTypeValue(index.intValue());
}
int colon = descriptor.indexOf(':');
String nameString = descriptor.substring(0, colon);
String descriptorString = descriptor.substring(colon + 1);
CPUTF8 name = cpUTF8Value(nameString, true);
CPUTF8 descriptorU = cpUTF8Value(descriptorString, true);
cpNameAndType = new CPNameAndType(name, descriptorU, -1 + descrOffset);
descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
}
return cpNameAndType;
}
public int[] getCpDescriptorNameInts() {
return cpDescriptorNameInts;
}
public int[] getCpDescriptorTypeInts() {
return cpDescriptorTypeInts;
}
}

@ -0,0 +1,127 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
/**
* Parses the file band headers (not including the actual bits themselves). At
* the end of this parse call, the input stream will be positioned at the start
* of the file_bits themselves, and there will be Sum(file_size) bits remaining
* in the stream with BYTE1 compression. A decent implementation will probably
* just stream the bytes out to the reconstituted Jar rather than caching them.
*/
public class FileBands extends BandSet {
private byte[][] fileBits;
private int[] fileModtime;
private String[] fileName;
private int[] fileOptions;
private long[] fileSize;
private final String[] cpUTF8;
private InputStream in;
/**
* @param segment
*/
public FileBands(Segment segment) {
super(segment);
this.cpUTF8 = segment.getCpBands().getCpUTF8();
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
public void read(InputStream in) throws IOException, Pack200Exception {
int numberOfFiles = header.getNumberOfFiles();
SegmentOptions options = header.getOptions();
fileName = parseReferences("file_name", in, Codec.UNSIGNED5,
numberOfFiles, cpUTF8);
fileSize = parseFlags("file_size", in, numberOfFiles, Codec.UNSIGNED5,
options.hasFileSizeHi());
if (options.hasFileModtime()) {
fileModtime = decodeBandInt("file_modtime", in, Codec.DELTA5,
numberOfFiles);
} else {
fileModtime = new int[numberOfFiles];
}
if (options.hasFileOptions()) {
fileOptions = decodeBandInt("file_options", in, Codec.UNSIGNED5,
numberOfFiles);
} else {
fileOptions = new int[numberOfFiles];
}
this.in = in; // store for use by processFileBits(), which is called
// later
}
// TODO: stream the file bits directly somehow
public void processFileBits() throws IOException, Pack200Exception {
// now read in the bytes
int numberOfFiles = header.getNumberOfFiles();
fileBits = new byte[numberOfFiles][];
for (int i = 0; i < numberOfFiles; i++) {
int size = (int) fileSize[i];
// TODO This breaks if file_size > 2^32. Probably an array is
// not the right choice, and we should just serialize it here?
fileBits[i] = new byte[size];
int read = in.read(fileBits[i]);
if (size != 0 && read < size) {
throw new Pack200Exception("Expected to read " + size
+ " bytes but read " + read);
}
}
}
public void unpack() {
}
public byte[][] getFileBits() {
return fileBits;
}
public int[] getFileModtime() {
return fileModtime;
}
public String[] getFileName() {
return fileName;
}
public int[] getFileOptions() {
return fileOptions;
}
public long[] getFileSize() {
return fileSize;
}
}

@ -0,0 +1,26 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
/**
* Interface for a class that can perform matching on flag values.
*/
public interface IMatcher {
public abstract boolean matches(long value);
}

@ -0,0 +1,249 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.ClassConstantPool;
import org.apache.harmony.unpack200.bytecode.ConstantPoolEntry;
/**
* Inner Class Bands
*/
public class IcBands extends BandSet {
private IcTuple[] icAll;
private final String[] cpUTF8;
private final String[] cpClass;
private Map thisClassToTuple;
private Map outerClassToTuples;
/**
* @param segment
*/
public IcBands(Segment segment) {
super(segment);
this.cpClass = segment.getCpBands().getCpClass();
this.cpUTF8 = segment.getCpBands().getCpUTF8();
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
public void read(InputStream in) throws IOException, Pack200Exception {
// Read IC bands
int innerClassCount = header.getInnerClassCount();
int[] icThisClassInts = decodeBandInt("ic_this_class", in,
Codec.UDELTA5, innerClassCount);
String[] icThisClass = getReferences(icThisClassInts, cpClass);
int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5,
innerClassCount);
int outerClasses = SegmentUtils.countBit16(icFlags);
int[] icOuterClassInts = decodeBandInt("ic_outer_class", in,
Codec.DELTA5, outerClasses);
String[] icOuterClass = new String[outerClasses];
for (int i = 0; i < icOuterClass.length; i++) {
if (icOuterClassInts[i] == 0) {
icOuterClass[i] = null;
} else {
icOuterClass[i] = cpClass[icOuterClassInts[i] - 1];
}
}
int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5,
outerClasses);
String[] icName = new String[outerClasses];
for (int i = 0; i < icName.length; i++) {
if (icNameInts[i] == 0) {
icName[i] = null;
} else {
icName[i] = cpUTF8[icNameInts[i] - 1];
}
}
// Construct IC tuples
icAll = new IcTuple[icThisClass.length];
int index = 0;
for (int i = 0; i < icThisClass.length; i++) {
String icTupleC = icThisClass[i];
int icTupleF = icFlags[i];
String icTupleC2 = null;
String icTupleN = null;
int cIndex = icThisClassInts[i];
int c2Index = -1;
int nIndex = -1;
if ((icFlags[i] & 1 << 16) != 0) {
icTupleC2 = icOuterClass[index];
icTupleN = icName[index];
c2Index = icOuterClassInts[index] - 1;
nIndex = icNameInts[index] - 1;
index++;
}
icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i);
}
}
public void unpack() throws IOException, Pack200Exception {
IcTuple[] allTuples = getIcTuples();
thisClassToTuple = new HashMap(allTuples.length);
outerClassToTuples = new HashMap(allTuples.length);
for(int index = 0; index < allTuples.length; index++) {
IcTuple tuple = allTuples[index];
// generate mapping thisClassString -> IcTuple
// presumably this relation is 1:1
//
Object result = thisClassToTuple.put(tuple.thisClassString(), tuple);
if (result != null) {
throw new Error("Collision detected in <thisClassString, IcTuple> mapping. " +
"There are at least two inner clases with the same name.");
}
// generate mapping outerClassString -> IcTuple
// this relation is 1:M
// If it's not anon and the outer is not anon, it could be relevant
if ( (!tuple.isAnonymous() && !tuple.outerIsAnonymous()) || (tuple.nestedExplicitFlagSet()) ) {
// add tuple to corresponding bucket
String key = tuple.outerClassString();
List bucket = (List)outerClassToTuples.get(key);
if (bucket == null) {
bucket = new ArrayList();
outerClassToTuples.put(key, bucket);
}
bucket.add(tuple);
}
}
}
public IcTuple[] getIcTuples() {
return icAll;
}
/**
* Answer the relevant IcTuples for the specified className and class
* constant pool.
*
* @param className
* String name of the class X for ic_relevant(X)
* @param cp
* ClassConstantPool used to generate ic_relevant(X)
* @return array of IcTuple
*/
public IcTuple[] getRelevantIcTuples(String className, ClassConstantPool cp) {
Set relevantTuplesContains = new HashSet();
List relevantTuples = new ArrayList();
List relevantCandidates = (List) outerClassToTuples.get(className);
if (relevantCandidates != null) {
for (int index = 0; index < relevantCandidates.size(); index++) {
IcTuple tuple = (IcTuple) relevantCandidates.get(index);
relevantTuplesContains.add(tuple);
relevantTuples.add(tuple);
}
}
List entries = cp.entries();
// For every class constant in both ic_this_class and cp,
// add it to ic_relevant. Repeat until no more
// changes to ic_relevant.
for (int eIndex = 0; eIndex < entries.size(); eIndex++) {
ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex);
if (entry instanceof CPClass) {
CPClass clazz = (CPClass) entry;
IcTuple relevant = (IcTuple) thisClassToTuple.get(clazz.name);
if (relevant != null && relevantTuplesContains.add(relevant)) {
relevantTuples.add(relevant);
}
}
}
// Not part of spec: fix up by adding to relevantTuples the parents
// of inner classes which are themselves inner classes.
// i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be
// added
// as well.
ArrayList tuplesToScan = new ArrayList(relevantTuples);
ArrayList tuplesToAdd = new ArrayList();
while (tuplesToScan.size() > 0) {
tuplesToAdd.clear();
for (int index = 0; index < tuplesToScan.size(); index++) {
IcTuple aRelevantTuple = (IcTuple) tuplesToScan.get(index);
IcTuple relevant = (IcTuple) thisClassToTuple
.get(aRelevantTuple.outerClassString());
if (relevant != null && !aRelevantTuple.outerIsAnonymous()) {
tuplesToAdd.add(relevant);
}
}
tuplesToScan.clear();
for (int index = 0; index < tuplesToAdd.size(); index++) {
IcTuple tuple = (IcTuple) tuplesToAdd.get(index);
if (relevantTuplesContains.add(tuple)) {
relevantTuples.add(tuple);
tuplesToScan.add(tuple);
}
}
}
// End not part of the spec. Ugh.
// Now order the result as a subsequence of ic_all
Collections.sort(relevantTuples, new Comparator() {
public int compare(Object arg0, Object arg1) {
Integer index1 = new Integer(((IcTuple)arg0).getTupleIndex());
Integer index2 = new Integer(((IcTuple)arg1).getTupleIndex());
return index1.compareTo(index2);
}
});
IcTuple[] relevantTuplesArray = new IcTuple[relevantTuples.size()];
for(int i = 0; i < relevantTuplesArray.length; i++) {
relevantTuplesArray[i] = (IcTuple)relevantTuples.get(i);
}
return relevantTuplesArray;
}
}

@ -0,0 +1,370 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.util.ArrayList;
/**
* An IcTuple is the set of information that describes an inner class.
*
* C is the fully qualified class name<br>
* F is the flags<br>
* C2 is the outer class name, or null if it can be inferred from C<br>
* N is the inner class name, or null if it can be inferred from C<br>
*/
public class IcTuple {
private final int cIndex;
private final int c2Index;
private final int nIndex;
private final int tIndex;
/**
*
* @param C
* @param F
* @param C2
* @param N
* @param cIndex the index of C in cpClass
* @param c2Index the index of C2 in cpClass, or -1 if C2 is null
* @param nIndex the index of N in cpUTF8, or -1 if N is null
*/
public IcTuple(String C, int F, String C2, String N, int cIndex, int c2Index, int nIndex, int tIndex) {
this.C = C;
this.F = F;
this.C2 = C2;
this.N = N;
this.cIndex = cIndex;
this.c2Index = c2Index;
this.nIndex = nIndex;
this.tIndex = tIndex;
if (null == N) {
predictSimple = true;
}
if (null == C2) {
predictOuter = true;
}
initializeClassStrings();
}
public IcTuple(String C, int F, int cIndex) {
this(C, F, null, null, cIndex, -1, -1, -1);
}
public static final int NESTED_CLASS_FLAG = 0x00010000;
protected String C; // this class
protected int F; // flags
protected String C2; // outer class
protected String N; // name
private boolean predictSimple;
private boolean predictOuter;
private String cachedOuterClassString;
private String cachedSimpleClassName;
private boolean initialized;
private boolean anonymous;
private boolean outerIsAnonymous;
private boolean member = true;
private int cachedOuterClassIndex = -1;
private int cachedSimpleClassNameIndex = -1;
/**
* Answer true if the receiver is predicted; answer false if the receiver is
* specified explicitly in the outer and name fields.
*/
public boolean predicted() {
return predictOuter || predictSimple;
}
/**
* Answer true if the receiver's bit 16 is set (indicating
* that explicit outer class and name fields are set).
* @return boolean
*/
public boolean nestedExplicitFlagSet() {
return (F & NESTED_CLASS_FLAG) == NESTED_CLASS_FLAG;
}
/**
* Break the receiver into components at $ boundaries.
*/
public String[] innerBreakAtDollar(String className) {
ArrayList resultList = new ArrayList();
int start = 0;
int index = 0;
while (index < className.length()) {
if (className.charAt(index) <= '$') {
resultList.add(className.substring(start, index));
start = index + 1;
}
index++;
if (index >= className.length()) {
// Add the last element
resultList.add(className.substring(start, className.length()));
}
}
String[] result = new String[resultList.size()];
for (int i = 0; i < resultList.size(); i++) {
result[i] = (String) resultList.get(i);
}
return result;
}
/**
* Answer the outer class name for the receiver. This may either be
* specified or inferred from inner class name.
*
* @return String name of outer class
*/
public String outerClassString() {
return cachedOuterClassString;
}
/**
* Answer the inner class name for the receiver.
*
* @return String name of inner class
*/
public String simpleClassName() {
return cachedSimpleClassName;
}
/**
* Answer the full name of the inner class represented by this tuple
* (including its outer component)
*
* @return String full name of inner class
*/
public String thisClassString() {
if (predicted()) {
return C;
} else {
// TODO: this may not be right. What if I
// get a class like Foo#Bar$Baz$Bug?
return C2 + "$" + N;
}
}
public boolean isMember() {
return member;
}
public boolean isAnonymous() {
return anonymous;
}
public boolean outerIsAnonymous() {
return outerIsAnonymous;
}
private boolean computeOuterIsAnonymous() {
String[] result = innerBreakAtDollar(cachedOuterClassString);
if (result.length == 0) {
throw new Error(
"Should have an outer before checking if it's anonymous");
}
for (int index = 0; index < result.length; index++) {
if (isAllDigits(result[index])) {
return true;
}
}
return false;
}
private void initializeClassStrings() {
if (initialized) {
return;
}
initialized = true;
if (!predictSimple) {
cachedSimpleClassName = N;
}
if (!predictOuter) {
cachedOuterClassString = C2;
}
// Class names must be calculated from
// this class name.
String nameComponents[] = innerBreakAtDollar(C);
if (nameComponents.length == 0) {
// Unable to predict outer class
// throw new Error("Unable to predict outer class name: " + C);
}
if (nameComponents.length == 1) {
// Unable to predict simple class name
// throw new Error("Unable to predict inner class name: " + C);
}
if (nameComponents.length < 2) {
// If we get here, we hope cachedSimpleClassName
// and cachedOuterClassString were caught by the
// predictSimple / predictOuter code above.
return;
}
// If we get to this point, nameComponents.length must be >=2
int lastPosition = nameComponents.length - 1;
cachedSimpleClassName = nameComponents[lastPosition];
cachedOuterClassString = "";
for (int index = 0; index < lastPosition; index++) {
cachedOuterClassString += nameComponents[index];
if (isAllDigits(nameComponents[index])) {
member = false;
}
if (index + 1 != lastPosition) {
// TODO: might need more logic to handle
// classes with separators of non-$ characters
// (ie Foo#Bar)
cachedOuterClassString += '$';
}
}
// TODO: these two blocks are the same as blocks
// above. Can we eliminate some by reworking the logic?
if (!predictSimple) {
cachedSimpleClassName = N;
cachedSimpleClassNameIndex = nIndex;
}
if (!predictOuter) {
cachedOuterClassString = C2;
cachedOuterClassIndex = c2Index;
}
if (isAllDigits(cachedSimpleClassName)) {
anonymous = true;
member = false;
if (nestedExplicitFlagSet()) {
// Predicted class - marking as member
member = true;
}
}
outerIsAnonymous = computeOuterIsAnonymous();
}
private boolean isAllDigits(String nameString) {
// Answer true if the receiver is all digits; otherwise answer false.
if (null == nameString) {
return false;
}
for (int index = 0; index < nameString.length(); index++) {
if (!Character.isDigit(nameString.charAt(index))) {
return false;
}
}
return true;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("IcTuple ");
result.append('(');
result.append(simpleClassName());
result.append(" in ");
result.append(outerClassString());
result.append(')');
return result.toString();
}
public boolean nullSafeEquals(String stringOne, String stringTwo) {
if (null == stringOne) {
return null == stringTwo;
}
return stringOne.equals(stringTwo);
}
public boolean equals(Object object) {
if ((object == null) || (object.getClass() != this.getClass())) {
return false;
}
IcTuple compareTuple = (IcTuple) object;
if (!nullSafeEquals(this.C, compareTuple.C)) {
return false;
}
if (!nullSafeEquals(this.C2, compareTuple.C2)) {
return false;
}
if (!nullSafeEquals(this.N, compareTuple.N)) {
return false;
}
return true;
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
cachedHashCode = 17;
if(C != null) { cachedHashCode =+ C.hashCode(); }
if(C2 != null) { cachedHashCode =+ C2.hashCode(); }
if(N != null) { cachedHashCode =+ N.hashCode(); }
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
public String getC() {
return C;
}
public int getF() {
return F;
}
public String getC2() {
return C2;
}
public String getN() {
return N;
}
public int getTupleIndex() {
return tIndex;
}
public String realOuterClassString() {
int firstDollarPosition = cachedOuterClassString.indexOf('$');
if (firstDollarPosition <= 0) {
return cachedOuterClassString;
}
return cachedOuterClassString.substring(0, firstDollarPosition);
}
public int thisClassIndex() {
if(predicted()) {
return cIndex;
} else {
return -1;
}
}
public int outerClassIndex() {
return cachedOuterClassIndex;
}
public int simpleClassNameIndex() {
return cachedSimpleClassNameIndex;
}
}

@ -0,0 +1,252 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.harmony.unpack200.bytecode.AnnotationDefaultAttribute;
import org.apache.harmony.unpack200.bytecode.Attribute;
import org.apache.harmony.unpack200.bytecode.CPDouble;
import org.apache.harmony.unpack200.bytecode.CPFloat;
import org.apache.harmony.unpack200.bytecode.CPInteger;
import org.apache.harmony.unpack200.bytecode.CPLong;
import org.apache.harmony.unpack200.bytecode.CPUTF8;
import org.apache.harmony.unpack200.bytecode.RuntimeVisibleorInvisibleAnnotationsAttribute;
import org.apache.harmony.unpack200.bytecode.RuntimeVisibleorInvisibleParameterAnnotationsAttribute;
import org.apache.harmony.unpack200.bytecode.AnnotationsAttribute.Annotation;
import org.apache.harmony.unpack200.bytecode.AnnotationsAttribute.ElementValue;
import org.apache.harmony.unpack200.bytecode.RuntimeVisibleorInvisibleParameterAnnotationsAttribute.ParameterAnnotation;
/**
* A group of metadata bands, such as class_RVA_bands, method_AD_bands etc.
*/
public class MetadataBandGroup {
private final String type;
private final CpBands cpBands;
private static CPUTF8 rvaUTF8;
private static CPUTF8 riaUTF8;
private static CPUTF8 rvpaUTF8;
private static CPUTF8 ripaUTF8;
public static void setRvaAttributeName(CPUTF8 cpUTF8Value) {
rvaUTF8 = cpUTF8Value;
}
public static void setRiaAttributeName(CPUTF8 cpUTF8Value) {
riaUTF8 = cpUTF8Value;
}
public static void setRvpaAttributeName(CPUTF8 cpUTF8Value) {
rvpaUTF8 = cpUTF8Value;
}
public static void setRipaAttributeName(CPUTF8 cpUTF8Value) {
ripaUTF8 = cpUTF8Value;
}
public MetadataBandGroup(String type, CpBands cpBands) {
this.type = type;
this.cpBands = cpBands;
}
private List attributes;
public int[] param_NB;
public int[] anno_N;
public CPUTF8[][] type_RS;
public int[][] pair_N;
public CPUTF8[] name_RU;
public int[] T;
public CPInteger[] caseI_KI;
public CPDouble[] caseD_KD;
public CPFloat[] caseF_KF;
public CPLong[] caseJ_KJ;
public CPUTF8[] casec_RS;
public String[] caseet_RS;
public String[] caseec_RU;
public CPUTF8[] cases_RU;
public int[] casearray_N;
public CPUTF8[] nesttype_RS;
public int[] nestpair_N;
public CPUTF8[] nestname_RU;
private int caseI_KI_Index;
private int caseD_KD_Index;
private int caseF_KF_Index;
private int caseJ_KJ_Index;
private int casec_RS_Index;
private int caseet_RS_Index;
private int caseec_RU_Index;
private int cases_RU_Index;
private int casearray_N_Index;
private int T_index;
private int nesttype_RS_Index;
private int nestpair_N_Index;
private Iterator nestname_RU_Iterator;
private int anno_N_Index;
private Iterator type_RS_Iterator;
private int pair_N_Index;
public List getAttributes() {
// TODO: Optimize iterators!
if (attributes == null) {
attributes = new ArrayList();
if (name_RU != null) {
Iterator name_RU_Iterator = Arrays.asList(name_RU).iterator();
if (!type.equals("AD")) {
T_index = 0;
}
caseI_KI_Index = 0;
caseD_KD_Index = 0;
caseF_KF_Index = 0;
caseJ_KJ_Index = 0;
casec_RS_Index = 0;
caseet_RS_Index = 0;
caseec_RU_Index = 0;
cases_RU_Index = 0;
casearray_N_Index = 0;
nesttype_RS_Index = 0;
nestpair_N_Index = 0;
nestname_RU_Iterator = Arrays.asList(nestname_RU).iterator();
if (type.equals("RVA") || type.equals("RIA")) {
for (int i = 0; i < anno_N.length; i++) {
attributes.add(getAttribute(anno_N[i], type_RS[i],
pair_N[i], name_RU_Iterator));
}
} else if (type.equals("RVPA") || type.equals("RIPA")) {
anno_N_Index = 0;
type_RS_Iterator = Arrays.asList(type_RS).iterator();
pair_N_Index = 0;
for (int i = 0; i < param_NB.length; i++) {
attributes.add(getParameterAttribute(param_NB[i],
name_RU_Iterator));
}
} else { // type.equals("AD")
for (int i = 0; i < T.length; i++) {
attributes.add(new AnnotationDefaultAttribute(
new ElementValue(T[i], getNextValue(T[i]))));
}
}
}
}
return attributes;
}
private Attribute getAttribute(int numAnnotations, CPUTF8[] types,
int[] pairCounts, Iterator namesIterator) {
Annotation[] annotations = new Annotation[numAnnotations];
for (int i = 0; i < numAnnotations; i++) {
annotations[i] = getAnnotation(types[i], pairCounts[i],
namesIterator);
}
return new RuntimeVisibleorInvisibleAnnotationsAttribute(type
.equals("RVA") ? rvaUTF8 : riaUTF8, annotations);
}
private Attribute getParameterAttribute(int numParameters,
Iterator namesIterator) {
ParameterAnnotation[] parameter_annotations = new ParameterAnnotation[numParameters];
for (int i = 0; i < numParameters; i++) {
int numAnnotations = anno_N[anno_N_Index++];
int[] pairCounts = pair_N[pair_N_Index++];
Annotation[] annotations = new Annotation[numAnnotations];
for (int j = 0; j < annotations.length; j++) {
annotations[j] = getAnnotation(
(CPUTF8) type_RS_Iterator.next(), pairCounts[j],
namesIterator);
}
parameter_annotations[i] = new ParameterAnnotation(annotations);
}
return new RuntimeVisibleorInvisibleParameterAnnotationsAttribute(type
.equals("RVPA") ? rvpaUTF8 : ripaUTF8,
parameter_annotations);
}
private Annotation getAnnotation(CPUTF8 type, int pairCount,
Iterator namesIterator) {
CPUTF8[] elementNames = new CPUTF8[pairCount];
ElementValue[] elementValues = new ElementValue[pairCount];
for (int j = 0; j < elementNames.length; j++) {
elementNames[j] = (CPUTF8) namesIterator.next();
int t = T[T_index++];
elementValues[j] = new ElementValue(t, getNextValue(t));
}
return new Annotation(pairCount, type, elementNames, elementValues);
}
private Object getNextValue(int t) {
switch (t) {
case 'B':
case 'C':
case 'I':
case 'S':
case 'Z':
return caseI_KI[caseI_KI_Index++];
case 'D':
return caseD_KD[caseD_KD_Index++];
case 'F':
return caseF_KF[caseF_KF_Index++];
case 'J':
return caseJ_KJ[caseJ_KJ_Index++];
case 'c':
return casec_RS[casec_RS_Index++];
case 'e':
// TODO: check this - it may not work if the first string already
// has a colon in it
String enumString = caseet_RS[caseet_RS_Index++] + ":"
+ caseec_RU[caseec_RU_Index++];
return cpBands.cpNameAndTypeValue(enumString);
case 's':
return cases_RU[cases_RU_Index++];
case '[':
int arraySize = casearray_N[casearray_N_Index++];
ElementValue[] nestedArray = new ElementValue[arraySize];
for (int i = 0; i < arraySize; i++) {
int nextT = T[T_index++];
nestedArray[i] = new ElementValue(nextT, getNextValue(nextT));
}
return nestedArray;
case '@':
CPUTF8 type = nesttype_RS[nesttype_RS_Index++];
int numPairs = nestpair_N[nestpair_N_Index++];
return getAnnotation(type, numPairs, nestname_RU_Iterator);
}
return null;
}
}

@ -0,0 +1,995 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.harmony.pack200.BHSDCodec;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.Attribute;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.CPDouble;
import org.apache.harmony.unpack200.bytecode.CPFieldRef;
import org.apache.harmony.unpack200.bytecode.CPFloat;
import org.apache.harmony.unpack200.bytecode.CPInteger;
import org.apache.harmony.unpack200.bytecode.CPInterfaceMethodRef;
import org.apache.harmony.unpack200.bytecode.CPLong;
import org.apache.harmony.unpack200.bytecode.CPMethodRef;
import org.apache.harmony.unpack200.bytecode.CPNameAndType;
import org.apache.harmony.unpack200.bytecode.CPString;
import org.apache.harmony.unpack200.bytecode.CPUTF8;
import org.apache.harmony.unpack200.bytecode.NewAttribute;
/**
* Set of bands relating to a non-predefined attribute
*/
public class NewAttributeBands extends BandSet {
private final AttributeLayout attributeLayout;
private int backwardsCallCount;
private List attributeLayoutElements;
public NewAttributeBands(Segment segment, AttributeLayout attributeLayout)
throws IOException {
super(segment);
this.attributeLayout = attributeLayout;
parseLayout();
attributeLayout.setBackwardsCallCount(backwardsCallCount);
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
public void read(InputStream in) throws IOException, Pack200Exception {
// does nothing - use parseAttributes instead
}
/**
* Parse the bands relating to this AttributeLayout and return the correct
* class file attributes as a List of {@link Attribute}
*
* @throws Pack200Exception
*/
public List parseAttributes(InputStream in, int occurrenceCount)
throws IOException, Pack200Exception {
for(int i = 0; i < attributeLayoutElements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i);
element.readBands(in, occurrenceCount);
}
List attributes = new ArrayList(occurrenceCount);
for (int i = 0; i < occurrenceCount; i++) {
attributes.add(getOneAttribute(i, attributeLayoutElements));
}
return attributes;
}
/**
* Get one attribute at the given index from the various bands. The correct
* bands must have already been read in.
*
* @param index
* @param elements
* @return
*/
private Attribute getOneAttribute(int index, List elements) {
NewAttribute attribute = new NewAttribute(segment.getCpBands()
.cpUTF8Value(attributeLayout.getName()),
attributeLayout.getIndex());
for(int i = 0; i < elements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) elements.get(i);
element.addToAttribute(index, attribute);
}
return attribute;
}
/**
* Tokenise the layout into AttributeElements
*
* @throws IOException
*/
private void parseLayout() throws IOException {
if (attributeLayoutElements == null) {
attributeLayoutElements = new ArrayList();
StringReader stream = new StringReader(attributeLayout.getLayout());
AttributeLayoutElement e;
while ((e = readNextAttributeElement(stream)) != null) {
attributeLayoutElements.add(e);
}
resolveCalls();
}
}
/**
* Resolve calls in the attribute layout and returns the number of backwards
* calls
*/
private void resolveCalls() {
int backwardsCalls = 0;
for (int i = 0; i < attributeLayoutElements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
.get(i);
if (element instanceof Callable) {
Callable callable = (Callable) element;
if(i == 0) {
callable.setFirstCallable(true);
}
List body = callable.body; // Look for calls in the body
for(int iIndex = 0; iIndex < body.size(); iIndex++) {
LayoutElement layoutElement = (LayoutElement) body
.get(iIndex);
// Set the callable for each call
backwardsCalls += resolveCallsForElement(i, callable,
layoutElement);
}
}
}
backwardsCallCount = backwardsCalls;
}
private int resolveCallsForElement(int i,
Callable currentCallable, LayoutElement layoutElement) {
int backwardsCalls = 0;
if (layoutElement instanceof Call) {
Call call = (Call) layoutElement;
int index = call.callableIndex;
if (index == 0) { // Calls the parent callable
backwardsCalls++;
call.setCallable(currentCallable);
} else if (index > 0) { // Forwards call
for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
.get(k);
if (el instanceof Callable) {
index--;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
} else { // Backwards call
backwardsCalls++;
for (int k = i; k >= 0; k--) {
AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
.get(k);
if (el instanceof Callable) {
index++;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
}
} else if (layoutElement instanceof Replication) {
List children = ((Replication)layoutElement).layoutElements;
for (Iterator iterator = children.iterator(); iterator.hasNext();) {
LayoutElement object = (LayoutElement) iterator.next();
backwardsCalls += resolveCallsForElement(i, currentCallable, object);
}
}
return backwardsCalls;
}
private AttributeLayoutElement readNextAttributeElement(StringReader stream)
throws IOException {
stream.mark(1);
int nextChar = stream.read();
if (nextChar == -1) {
return null;
}
if (nextChar == '[') {
List body = readBody(getStreamUpToMatchingBracket(stream));
return new Callable(body);
} else {
stream.reset();
return readNextLayoutElement(stream);
}
}
private LayoutElement readNextLayoutElement(StringReader stream)
throws IOException {
int nextChar = stream.read();
if (nextChar == -1) {
return null;
}
switch (nextChar) {
// Integrals
case 'B':
case 'H':
case 'I':
case 'V':
return new Integral(new String(new char[] { (char)nextChar }));
case 'S':
case 'F':
return new Integral(new String(new char[] { (char)nextChar,
(char) stream.read() }));
case 'P':
stream.mark(1);
if (stream.read() != 'O') {
stream.reset();
return new Integral("P" + (char) stream.read());
} else {
return new Integral("PO" + (char) stream.read());
}
case 'O':
stream.mark(1);
if (stream.read() != 'S') {
stream.reset();
return new Integral("O" + (char) stream.read());
} else {
return new Integral("OS" + (char) stream.read());
}
// Replication
case 'N':
char uint_type = (char) stream.read();
stream.read(); // '['
String str = readUpToMatchingBracket(stream);
return new Replication("" + uint_type, str);
// Union
case 'T':
String int_type = "" + (char) stream.read();
if (int_type.equals("S")) {
int_type += (char) stream.read();
}
List unionCases = new ArrayList();
UnionCase c;
while ((c = readNextUnionCase(stream)) != null) {
unionCases.add(c);
}
stream.read(); // '('
stream.read(); // ')'
stream.read(); // '['
List body = null;
stream.mark(1);
char next = (char) stream.read();
if (next != ']') {
stream.reset();
body = readBody(getStreamUpToMatchingBracket(stream));
}
return new Union(int_type, unionCases, body);
// Call
case '(':
int number = readNumber(stream).intValue();
stream.read(); // ')'
return new Call(number);
// Reference
case 'K':
case 'R':
String string = "" + (char)nextChar + (char) stream.read();
char nxt = (char) stream.read();
string += nxt;
if (nxt == 'N') {
string += (char) stream.read();
}
return new Reference(string);
}
return null;
}
/**
* Read a UnionCase from the stream
*
* @param stream
* @return
* @throws IOException
*/
private UnionCase readNextUnionCase(StringReader stream) throws IOException {
stream.mark(2);
stream.read(); // '('
char next = (char) stream.read();
if (next == ')') {
stream.reset();
return null;
} else {
stream.reset();
stream.read(); // '('
}
List tags = new ArrayList();
Integer nextTag;
do {
nextTag = readNumber(stream);
if(nextTag != null) {
tags.add(nextTag);
stream.read(); // ',' or ')'
}
} while (nextTag != null);
stream.read(); // '['
stream.mark(1);
next = (char) stream.read();
if (next == ']') {
return new UnionCase(tags);
} else {
stream.reset();
return new UnionCase(tags,
readBody(getStreamUpToMatchingBracket(stream)));
}
}
/**
* An AttributeLayoutElement is a part of an attribute layout and has one or
* more bands associated with it, which transmit the AttributeElement data
* for successive Attributes of this type.
*/
private interface AttributeLayoutElement {
/**
* Read the bands associated with this part of the layout
*
* @param in
* @param count
* @throws Pack200Exception
* @throws IOException
*/
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception;
/**
* Add the band data for this element at the given index to the
* attribute
*
* @param index
* @param attribute
*/
public void addToAttribute(int index, NewAttribute attribute);
}
private abstract class LayoutElement implements AttributeLayoutElement {
protected int getLength(char uint_type) {
int length = 0;
switch (uint_type) {
case 'B':
length = 1;
break;
case 'H':
length = 2;
break;
case 'I':
length = 4;
break;
case 'V':
length = 0;
break;
}
return length;
}
}
private class Integral extends LayoutElement {
private final String tag;
private int[] band;
public Integral(String tag) {
this.tag = tag;
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
band = decodeBandInt(attributeLayout.getName() + "_" + tag, in,
getCodec(tag), count);
}
public void addToAttribute(int n, NewAttribute attribute) {
long value = band[n];
if (tag.equals("B") || tag.equals("FB")) {
attribute.addInteger(1, value);
} else if (tag.equals("SB")) {
attribute.addInteger(1, (byte) value);
} else if (tag.equals("H") || tag.equals("FH")) {
attribute.addInteger(2, value);
} else if (tag.equals("SH")) {
attribute.addInteger(2, (short) value);
} else if (tag.equals("I") || tag.equals("FI")) {
attribute.addInteger(4, value);
} else if (tag.equals("SI")) {
attribute.addInteger(4, (int) value);
} else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
// Don't add V's - they shouldn't be written out to the class
// file
} else if (tag.startsWith("PO")) {
char uint_type = tag.substring(2).toCharArray()[0];
int length = getLength(uint_type);
attribute.addBCOffset(length, (int) value);
} else if (tag.startsWith("P")) {
char uint_type = tag.substring(1).toCharArray()[0];
int length = getLength(uint_type);
attribute.addBCIndex(length, (int) value);
} else if (tag.startsWith("OS")) {
char uint_type = tag.substring(2).toCharArray()[0];
int length = getLength(uint_type);
if (length == 1) {
value = (byte) value;
} else if (length == 2) {
value = (short) value;
} else if (length == 4) {
value = (int) value;
}
attribute.addBCLength(length, (int) value);
} else if (tag.startsWith("O")) {
char uint_type = tag.substring(1).toCharArray()[0];
int length = getLength(uint_type);
attribute.addBCLength(length, (int) value);
}
}
long getValue(int index) {
return band[index];
}
}
/**
* A replication is an array of layout elements, with an associated count
*/
private class Replication extends LayoutElement {
private final Integral countElement;
private final List layoutElements = new ArrayList();
public Replication(String tag, String contents) throws IOException {
this.countElement = new Integral(tag);
StringReader stream = new StringReader(contents);
LayoutElement e;
while ((e = readNextLayoutElement(stream)) != null) {
layoutElements.add(e);
}
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
countElement.readBands(in, count);
int arrayCount = 0;
for (int i = 0; i < count; i++) {
arrayCount += countElement.getValue(i);
}
for(int i = 0; i < layoutElements.size(); i++) {
LayoutElement element = (LayoutElement) layoutElements.get(i);
element.readBands(in, arrayCount);
}
}
public void addToAttribute(int index, NewAttribute attribute) {
// Add the count value
countElement.addToAttribute(index, attribute);
// Add the corresponding array values
int offset = 0;
for (int i = 0; i < index; i++) {
offset += countElement.getValue(i);
}
long numElements = countElement.getValue(index);
for (int i = offset; i < offset + numElements; i++) {
for (int it = 0; it < layoutElements.size(); it++) {
LayoutElement element = (LayoutElement) layoutElements.get(it);
element.addToAttribute(i, attribute);
}
}
}
}
/**
* A Union is a type of layout element where the tag value acts as a
* selector for one of the union cases
*/
private class Union extends LayoutElement {
private final Integral unionTag;
private final List unionCases;
private final List defaultCaseBody;
private int[] caseCounts;
private int defaultCount;
public Union(String tag, List unionCases, List body) {
this.unionTag = new Integral(tag);
this.unionCases = unionCases;
this.defaultCaseBody = body;
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
unionTag.readBands(in, count);
int[] values = unionTag.band;
// Count the band size for each union case then read the bands
caseCounts = new int[unionCases.size()];
for (int i = 0; i < caseCounts.length; i++) {
UnionCase unionCase = (UnionCase) unionCases.get(i);
for (int j = 0; j < values.length; j++) {
if (unionCase.hasTag(values[j])) {
caseCounts[i]++;
}
}
unionCase.readBands(in, caseCounts[i]);
}
// Count number of default cases then read the default bands
for (int i = 0; i < values.length; i++) {
boolean found = false;
for(int it = 0; it < unionCases.size(); it++) {
UnionCase unionCase = (UnionCase) unionCases.get(it);
if (unionCase.hasTag(values[i])) {
found = true;
}
}
if (!found) {
defaultCount++;
}
}
if (defaultCaseBody != null) {
for(int i = 0; i < defaultCaseBody.size(); i++) {
LayoutElement element = (LayoutElement) defaultCaseBody.get(i);
element.readBands(in, defaultCount);
}
}
}
public void addToAttribute(int n, NewAttribute attribute) {
unionTag.addToAttribute(n, attribute);
int offset = 0;
int[] tagBand = unionTag.band;
long tag = unionTag.getValue(n);
boolean defaultCase = true;
for(int i = 0; i < unionCases.size(); i++) {
UnionCase element = (UnionCase) unionCases.get(i);
if (element.hasTag(tag)) {
defaultCase = false;
for (int j = 0; j < n; j++) {
if (element.hasTag(tagBand[j])) {
offset++;
}
}
element.addToAttribute(offset, attribute);
}
}
if (defaultCase) {
// default case
int defaultOffset = 0;
for (int j = 0; j < n; j++) {
boolean found = false;
for(int i = 0; i < unionCases.size(); i++) {
UnionCase element = (UnionCase) unionCases.get(i);
if (element.hasTag(tagBand[j])) {
found = true;
}
}
if (!found) {
defaultOffset++;
}
}
if (defaultCaseBody != null) {
for(int i = 0; i < defaultCaseBody.size(); i++) {
LayoutElement element = (LayoutElement) defaultCaseBody.get(i);
element.addToAttribute(defaultOffset, attribute);
}
}
}
}
}
private class Call extends LayoutElement {
private final int callableIndex;
private Callable callable;
public Call(int callableIndex) {
this.callableIndex = callableIndex;
}
public void setCallable(Callable callable) {
this.callable = callable;
if (callableIndex < 1) {
callable.setBackwardsCallable();
}
}
public void readBands(InputStream in, int count) {
/*
* We don't read anything here, but we need to pass the extra count
* to the callable if it's a forwards call. For backwards callables
* the count is transmitted directly in the attribute bands and so
* it is added later.
*/
if (callableIndex > 0) {
callable.addCount(count);
}
}
public void addToAttribute(int n, NewAttribute attribute) {
callable.addNextToAttribute(attribute);
}
}
/**
* Constant Pool Reference
*/
private class Reference extends LayoutElement {
private final String tag;
private Object band;
private final int length;
public Reference(String tag) {
this.tag = tag;
length = getLength(tag.charAt(tag.length() - 1));
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
if (tag.startsWith("KI")) { // Integer
band = parseCPIntReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("KJ")) { // Long
band = parseCPLongReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("KF")) { // Float
band = parseCPFloatReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("KD")) { // Double
band = parseCPDoubleReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("KS")) { // String
band = parseCPStringReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("RC")) { // Class
band = parseCPClassReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("RS")) { // Signature
band = parseCPSignatureReferences(attributeLayout.getName(),
in, Codec.UNSIGNED5, count);
} else if (tag.startsWith("RD")) { // Descriptor
band = parseCPDescriptorReferences(attributeLayout.getName(),
in, Codec.UNSIGNED5, count);
} else if (tag.startsWith("RF")) { // Field Reference
band = parseCPFieldRefReferences(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
} else if (tag.startsWith("RM")) { // Method Reference
band = parseCPMethodRefReferences(attributeLayout.getName(),
in, Codec.UNSIGNED5, count);
} else if (tag.startsWith("RI")) { // Interface Method Reference
band = parseCPInterfaceMethodRefReferences(attributeLayout
.getName(), in, Codec.UNSIGNED5, count);
} else if (tag.startsWith("RU")) { // UTF8 String
band = parseCPUTF8References(attributeLayout.getName(), in,
Codec.UNSIGNED5, count);
}
}
public void addToAttribute(int n, NewAttribute attribute) {
if (tag.startsWith("KI")) { // Integer
attribute.addCPConstant(length, ((CPInteger[]) band)[n]);
} else if (tag.startsWith("KJ")) { // Long
attribute.addCPConstant(length, ((CPLong[]) band)[n]);
} else if (tag.startsWith("KF")) { // Float
attribute.addCPConstant(length, ((CPFloat[]) band)[n]);
} else if (tag.startsWith("KD")) { // Double
attribute.addCPConstant(length, ((CPDouble[]) band)[n]);
} else if (tag.startsWith("KS")) { // String
attribute.addCPConstant(length, ((CPString[]) band)[n]);
} else if (tag.startsWith("RC")) { // Class
attribute.addCPClass(length, ((CPClass[]) band)[n]);
} else if (tag.startsWith("RS")) { // Signature
attribute.addCPUTF8(length, ((CPUTF8[]) band)[n]);
} else if (tag.startsWith("RD")) { // Descriptor
attribute.addCPNameAndType(length, ((CPNameAndType[]) band)[n]);
} else if (tag.startsWith("RF")) { // Field Reference
attribute.addCPFieldRef(length, ((CPFieldRef[]) band)[n]);
} else if (tag.startsWith("RM")) { // Method Reference
attribute.addCPMethodRef(length, ((CPMethodRef[]) band)[n]);
} else if (tag.startsWith("RI")) { // Interface Method Reference
attribute.addCPIMethodRef(length,
((CPInterfaceMethodRef[]) band)[n]);
} else if (tag.startsWith("RU")) { // UTF8 String
attribute.addCPUTF8(length, ((CPUTF8[]) band)[n]);
}
}
}
private static class Callable implements AttributeLayoutElement {
private final List body;
private boolean isBackwardsCallable;
private boolean isFirstCallable;
public Callable(List body) throws IOException {
this.body = body;
}
private int count;
private int index;
/**
* Used by calls when adding band contents to attributes so they don't
* have to keep track of the internal index of the callable
*
* @param attribute
*/
public void addNextToAttribute(NewAttribute attribute) {
for(int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.addToAttribute(index, attribute);
}
index++;
}
/**
* Adds the count of a call to this callable (ie the number of calls)
*
* @param count
*/
public void addCount(int count) {
this.count += count;
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
if(isFirstCallable) {
count += this.count;
} else {
count = this.count;
}
for(int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.readBands(in, count);
}
}
public void addToAttribute(int n, NewAttribute attribute) {
if(isFirstCallable) {
// Ignore n because bands also contain element parts from calls
for(int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.addToAttribute(index, attribute);
}
index++;
}
}
public boolean isBackwardsCallable() {
return isBackwardsCallable;
}
/**
* Tells this Callable that it is a backwards callable
*/
public void setBackwardsCallable() {
this.isBackwardsCallable = true;
}
public void setFirstCallable(boolean isFirstCallable) {
this.isFirstCallable = isFirstCallable;
}
}
/**
* A Union case
*/
private class UnionCase extends LayoutElement {
private List body;
private final List tags;
public UnionCase(List tags) {
this.tags = tags;
}
public boolean hasTag(long l) {
return tags.contains(new Integer((int) l));
}
public UnionCase(List tags, List body) throws IOException {
this.tags = tags;
this.body = body;
}
public void readBands(InputStream in, int count) throws IOException,
Pack200Exception {
if (body != null) {
for(int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.readBands(in, count);
}
}
}
public void addToAttribute(int index, NewAttribute attribute) {
if (body != null) {
for(int i = 0; i < body.size(); i++) {
LayoutElement element = (LayoutElement) body.get(i);
element.addToAttribute(index, attribute);
}
}
}
}
/**
* Utility method to get the contents of the given stream, up to the next
* ']', (ignoring pairs of brackets '[' and ']')
*
* @param stream
* @return
* @throws IOException
*/
private StringReader getStreamUpToMatchingBracket(StringReader stream)
throws IOException {
StringBuffer sb = new StringBuffer();
int foundBracket = -1;
while (foundBracket != 0) {
char c = (char) stream.read();
if (c == ']') {
foundBracket++;
}
if (c == '[') {
foundBracket--;
}
if (!(foundBracket == 0)) {
sb.append(c);
}
}
return new StringReader(sb.toString());
}
/**
* Returns the {@link BHSDCodec} that should be used for the given layout
* element
*
* @param layoutElement
*/
public BHSDCodec getCodec(String layoutElement) {
if (layoutElement.indexOf('O') >= 0) {
return Codec.BRANCH5;
} else if (layoutElement.indexOf('P') >= 0) {
return Codec.BCI5;
} else if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$
&& layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
return Codec.SIGNED5;
} else if (layoutElement.indexOf('B') >= 0) {
return Codec.BYTE1;
} else {
return Codec.UNSIGNED5;
}
}
/**
* Utility method to get the contents of the given stream, up to the next
* ']', (ignoring pairs of brackets '[' and ']')
*
* @param stream
* @return
* @throws IOException
*/
private String readUpToMatchingBracket(StringReader stream)
throws IOException {
StringBuffer sb = new StringBuffer();
int foundBracket = -1;
while (foundBracket != 0) {
char c = (char) stream.read();
if (c == ']') {
foundBracket++;
}
if (c == '[') {
foundBracket--;
}
if (!(foundBracket == 0)) {
sb.append(c);
}
}
return sb.toString();
}
/**
* Read a number from the stream and return it
*
* @param stream
* @return
* @throws IOException
*/
private Integer readNumber(StringReader stream) throws IOException {
stream.mark(1);
char first = (char) stream.read();
boolean negative = first == '-';
if (!negative) {
stream.reset();
}
stream.mark(100);
int i;
int length = 0;
while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) {
length++;
}
stream.reset();
if(length == 0) {
return null;
}
char[] digits = new char[length];
int read = stream.read(digits);
if (read != digits.length) {
throw new IOException("Error reading from the input stream");
}
return new Integer(Integer.parseInt((negative ? "-" : "") + new String(digits)));
}
/**
* Read a 'body' section of the layout from the given stream
*
* @param stream
* @return List of LayoutElements
* @throws IOException
*/
private List readBody(StringReader stream) throws IOException {
List layoutElements = new ArrayList();
LayoutElement e;
while ((e = readNextLayoutElement(stream)) != null) {
layoutElements.add(e);
}
return layoutElements;
}
public int getBackwardsCallCount() {
return backwardsCallCount;
}
/**
* Once the attribute bands have been read the callables can be informed
* about the number of times each is subject to a backwards call. This
* method is used to set this information.
*
* @param backwardsCalls
* one int for each backwards callable, which contains the number
* of times that callable is subject to a backwards call.
* @throws IOException
*/
public void setBackwardsCalls(int[] backwardsCalls) throws IOException {
int index = 0;
parseLayout();
for(int i = 0; i < attributeLayoutElements.size(); i++) {
AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i);
if (element instanceof Callable
&& ((Callable) element).isBackwardsCallable()) {
((Callable) element).addCount(backwardsCalls[index]);
index++;
}
}
}
public void unpack() throws IOException, Pack200Exception {
}
}

@ -0,0 +1,640 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.zip.CRC32;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.Attribute;
import org.apache.harmony.unpack200.bytecode.CPClass;
import org.apache.harmony.unpack200.bytecode.CPField;
import org.apache.harmony.unpack200.bytecode.CPMethod;
import org.apache.harmony.unpack200.bytecode.CPUTF8;
import org.apache.harmony.unpack200.bytecode.ClassConstantPool;
import org.apache.harmony.unpack200.bytecode.ClassFile;
import org.apache.harmony.unpack200.bytecode.ClassFileEntry;
import org.apache.harmony.unpack200.bytecode.InnerClassesAttribute;
import org.apache.harmony.unpack200.bytecode.SourceFileAttribute;
/**
* A Pack200 archive consists of one or more segments. Each segment is
* stand-alone, in the sense that every segment has the magic number header;
* thus, every segment is also a valid archive. However, it is possible to
* combine (non-GZipped) archives into a single large archive by concatenation
* alone. Thus all the hard work in unpacking an archive falls to understanding
* a segment.
*
* The first component of a segment is the header; this contains (amongst other
* things) the expected counts of constant pool entries, which in turn defines
* how many values need to be read from the stream. Because values are variable
* width (see {@link Codec}), it is not possible to calculate the start of the
* next segment, although one of the header values does hint at the size of the
* segment if non-zero, which can be used for buffering purposes.
*
* Note that this does not perform any buffering of the input stream; each value
* will be read on a byte-by-byte basis. It does not perform GZip decompression
* automatically; both of these are expected to be done by the caller if the
* stream has the magic header for GZip streams ({@link GZIPInputStream#GZIP_MAGIC}).
* In any case, if GZip decompression is being performed the input stream will
* be buffered at a higher level, and thus this can read on a byte-oriented
* basis.
*/
public class Segment {
public static final int LOG_LEVEL_VERBOSE = 2;
public static final int LOG_LEVEL_STANDARD = 1;
public static final int LOG_LEVEL_QUIET = 0;
private SegmentHeader header;
private CpBands cpBands;
private AttrDefinitionBands attrDefinitionBands;
private IcBands icBands;
private ClassBands classBands;
private BcBands bcBands;
private FileBands fileBands;
private boolean overrideDeflateHint;
private boolean deflateHint;
private boolean doPreRead;
private int logLevel;
private PrintWriter logStream;
private byte[][] classFilesContents;
private boolean[] fileDeflate;
private boolean[] fileIsClass;
private InputStream internalBuffer;
private ClassFile buildClassFile(int classNum) throws Pack200Exception {
ClassFile classFile = new ClassFile();
int[] major = classBands.getClassVersionMajor();
int[] minor = classBands.getClassVersionMinor();
if(major != null) {
classFile.major = major[classNum];
classFile.minor = minor[classNum];
} else {
classFile.major = header.getDefaultClassMajorVersion();
classFile.minor = header.getDefaultClassMinorVersion();
}
// build constant pool
ClassConstantPool cp = classFile.pool;
int fullNameIndexInCpClass = classBands.getClassThisInts()[classNum];
String fullName = cpBands.getCpClass()[fullNameIndexInCpClass];
// SourceFile attribute
int i = fullName.lastIndexOf("/") + 1; // if lastIndexOf==-1, then
// -1+1=0, so str.substring(0)
// == str
// Get the source file attribute
ArrayList classAttributes = classBands.getClassAttributes()[classNum];
SourceFileAttribute sourceFileAttribute = null;
for (int index = 0; index < classAttributes.size(); index++) {
if (((Attribute) classAttributes.get(index))
.isSourceFileAttribute()) {
sourceFileAttribute = ((SourceFileAttribute) classAttributes
.get(index));
}
}
if (sourceFileAttribute == null) {
// If we don't have a source file attribute yet, we need
// to infer it from the class.
AttributeLayout SOURCE_FILE = attrDefinitionBands
.getAttributeDefinitionMap().getAttributeLayout(
AttributeLayout.ATTRIBUTE_SOURCE_FILE,
AttributeLayout.CONTEXT_CLASS);
if (SOURCE_FILE.matches(classBands.getRawClassFlags()[classNum])) {
int firstDollar = SegmentUtils.indexOfFirstDollar(fullName);
String fileName = null;
if (firstDollar > -1 && (i <= firstDollar)) {
fileName = fullName.substring(i, firstDollar) + ".java";
} else {
fileName = fullName.substring(i) + ".java";
}
sourceFileAttribute = new SourceFileAttribute(cpBands
.cpUTF8Value(fileName, false));
classFile.attributes = new Attribute[] { (Attribute) cp
.add(sourceFileAttribute) };
} else {
classFile.attributes = new Attribute[] {};
}
} else {
classFile.attributes = new Attribute[] { (Attribute) cp
.add(sourceFileAttribute) };
}
// If we see any class attributes, add them to the class's attributes
// that will
// be written out. Keep SourceFileAttributes out since we just
// did them above.
ArrayList classAttributesWithoutSourceFileAttribute = new ArrayList(classAttributes.size());
for (int index = 0; index < classAttributes.size(); index++) {
Attribute attrib = (Attribute) classAttributes.get(index);
if (!attrib.isSourceFileAttribute()) {
classAttributesWithoutSourceFileAttribute.add(attrib);
}
}
Attribute[] originalAttributes = classFile.attributes;
classFile.attributes = new Attribute[originalAttributes.length
+ classAttributesWithoutSourceFileAttribute.size()];
System.arraycopy(originalAttributes, 0, classFile.attributes, 0,
originalAttributes.length);
for (int index = 0; index < classAttributesWithoutSourceFileAttribute
.size(); index++) {
Attribute attrib = ((Attribute) classAttributesWithoutSourceFileAttribute
.get(index));
cp.add(attrib);
classFile.attributes[originalAttributes.length + index] = attrib;
}
// this/superclass
ClassFileEntry cfThis = cp.add(cpBands.cpClassValue(fullNameIndexInCpClass));
ClassFileEntry cfSuper = cp.add(cpBands.cpClassValue(classBands
.getClassSuperInts()[classNum]));
// add interfaces
ClassFileEntry cfInterfaces[] = new ClassFileEntry[classBands
.getClassInterfacesInts()[classNum].length];
for (i = 0; i < cfInterfaces.length; i++) {
cfInterfaces[i] = cp.add(cpBands.cpClassValue(classBands
.getClassInterfacesInts()[classNum][i]));
}
// add fields
ClassFileEntry cfFields[] = new ClassFileEntry[classBands
.getClassFieldCount()[classNum]];
// fieldDescr and fieldFlags used to create this
for (i = 0; i < cfFields.length; i++) {
int descriptorIndex = classBands.getFieldDescrInts()[classNum][i];
int nameIndex = cpBands.getCpDescriptorNameInts()[descriptorIndex];
int typeIndex = cpBands.getCpDescriptorTypeInts()[descriptorIndex];
CPUTF8 name = cpBands.cpUTF8Value(nameIndex);
CPUTF8 descriptor = cpBands.cpSignatureValue(typeIndex);
cfFields[i] = cp.add(new CPField(name, descriptor, classBands
.getFieldFlags()[classNum][i], classBands
.getFieldAttributes()[classNum][i]));
}
// add methods
ClassFileEntry cfMethods[] = new ClassFileEntry[classBands
.getClassMethodCount()[classNum]];
// methodDescr and methodFlags used to create this
for (i = 0; i < cfMethods.length; i++) {
int descriptorIndex = classBands.getMethodDescrInts()[classNum][i];
// int colon = descriptorStr.indexOf(':');
int nameIndex = cpBands.getCpDescriptorNameInts()[descriptorIndex];
int typeIndex = cpBands.getCpDescriptorTypeInts()[descriptorIndex];
CPUTF8 name = cpBands.cpUTF8Value(nameIndex);
CPUTF8 descriptor = cpBands.cpSignatureValue(typeIndex);
cfMethods[i] = cp.add(new CPMethod(name, descriptor, classBands
.getMethodFlags()[classNum][i], classBands
.getMethodAttributes()[classNum][i]));
}
cp.addNestedEntries();
// add inner class attribute (if required)
boolean addInnerClassesAttr = false;
IcTuple[] ic_local = getClassBands().getIcLocal()[classNum];
boolean ic_local_sent = ic_local != null;
InnerClassesAttribute innerClassesAttribute = new InnerClassesAttribute(
"InnerClasses");
IcTuple[] ic_relevant = getIcBands().getRelevantIcTuples(fullName, cp);
List ic_stored = computeIcStored(ic_local, ic_relevant);
for (int index = 0; index < ic_stored.size(); index++) {
IcTuple icStored = (IcTuple)ic_stored.get(index);
int innerClassIndex = icStored.thisClassIndex();
int outerClassIndex = icStored.outerClassIndex();
int simpleClassNameIndex = icStored.simpleClassNameIndex();
String innerClassString = icStored.thisClassString();
String outerClassString = icStored.outerClassString();
String simpleClassName = icStored.simpleClassName();
CPClass innerClass = null;
CPUTF8 innerName = null;
CPClass outerClass = null;
innerClass = innerClassIndex != -1 ? cpBands
.cpClassValue(innerClassIndex) : cpBands
.cpClassValue(innerClassString);
if (!icStored.isAnonymous()) {
innerName = simpleClassNameIndex != -1 ? cpBands.cpUTF8Value(
simpleClassNameIndex) : cpBands
.cpUTF8Value(simpleClassName);
}
if (icStored.isMember()) {
outerClass = outerClassIndex != -1 ? cpBands
.cpClassValue(outerClassIndex) : cpBands
.cpClassValue(outerClassString);
}
int flags = icStored.F;
innerClassesAttribute.addInnerClassesEntry(innerClass, outerClass,
innerName, flags);
addInnerClassesAttr = true;
}
// If ic_local is sent and it's empty, don't add
// the inner classes attribute.
if (ic_local_sent && (ic_local.length == 0)) {
addInnerClassesAttr = false;
}
// If ic_local is not sent and ic_relevant is empty,
// don't add the inner class attribute.
if (!ic_local_sent && (ic_relevant.length == 0)) {
addInnerClassesAttr = false;
}
if (addInnerClassesAttr) {
// Need to add the InnerClasses attribute to the
// existing classFile attributes.
Attribute[] originalAttrs = classFile.attributes;
Attribute[] newAttrs = new Attribute[originalAttrs.length + 1];
for (int index = 0; index < originalAttrs.length; index++) {
newAttrs[index] = originalAttrs[index];
}
newAttrs[newAttrs.length - 1] = innerClassesAttribute;
classFile.attributes = newAttrs;
cp.addWithNestedEntries(innerClassesAttribute);
}
// sort CP according to cp_All
cp.resolve(this);
// NOTE the indexOf is only valid after the cp.resolve()
// build up remainder of file
classFile.accessFlags = (int) classBands.getClassFlags()[classNum];
classFile.thisClass = cp.indexOf(cfThis);
classFile.superClass = cp.indexOf(cfSuper);
// TODO placate format of file for writing purposes
classFile.interfaces = new int[cfInterfaces.length];
for (i = 0; i < cfInterfaces.length; i++) {
classFile.interfaces[i] = cp.indexOf(cfInterfaces[i]);
}
classFile.fields = cfFields;
classFile.methods = cfMethods;
return classFile;
}
/**
* Given an ic_local and an ic_relevant, use them to calculate what should
* be added as ic_stored.
*
* @param ic_local
* IcTuple[] array of local transmitted tuples
* @param ic_relevant
* IcTuple[] array of relevant tuples
* @return List of tuples to be stored. If ic_local is null or
* empty, the values returned may not be correct. The caller will
* have to determine if this is the case.
*/
private List computeIcStored(IcTuple[] ic_local, IcTuple[] ic_relevant) {
List result = new ArrayList(ic_relevant.length);
List duplicates = new ArrayList(ic_relevant.length);
Set isInResult = new HashSet(ic_relevant.length);
// need to compute:
// result = ic_local XOR ic_relevant
// add ic_local
if (ic_local != null) {
for(int index = 0; index < ic_local.length; index++) {
if (isInResult.add(ic_local[index])) {
result.add(ic_local[index]);
}
}
}
// add ic_relevant
for(int index = 0; index < ic_relevant.length; index++) {
if (isInResult.add(ic_relevant[index])) {
result.add(ic_relevant[index]);
} else {
duplicates.add(ic_relevant[index]);
}
}
// eliminate "duplicates"
for(int index = 0; index < duplicates.size(); index++) {
IcTuple tuple = (IcTuple)duplicates.get(index);
result.remove(tuple);
}
return result;
}
/**
* This performs reading the data from the stream into non-static instance of
* Segment. After the completion of this method stream can be freed.
*
* @param in
* the input stream to read from
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void readSegment(InputStream in) throws IOException,
Pack200Exception {
log(LOG_LEVEL_VERBOSE, "-------");
cpBands = new CpBands(this);
cpBands.read(in);
attrDefinitionBands = new AttrDefinitionBands(this);
attrDefinitionBands.read(in);
icBands = new IcBands(this);
icBands.read(in);
classBands = new ClassBands(this);
classBands.read(in);
bcBands = new BcBands(this);
bcBands.read(in);
fileBands = new FileBands(this);
fileBands.read(in);
fileBands.processFileBits();
}
/**
* This performs the actual work of parsing against a non-static instance of
* Segment. This method is intended to run concurrently for multiple segments.
*
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void parseSegment() throws IOException, Pack200Exception {
header.unpack();
cpBands.unpack();
attrDefinitionBands.unpack();
icBands.unpack();
classBands.unpack();
bcBands.unpack();
fileBands.unpack();
int classNum = 0;
int numberOfFiles = header.getNumberOfFiles();
String[] fileName = fileBands.getFileName();
int[] fileOptions = fileBands.getFileOptions();
SegmentOptions options = header.getOptions();
classFilesContents = new byte[numberOfFiles][];
fileDeflate = new boolean[numberOfFiles];
fileIsClass = new boolean[numberOfFiles];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
for (int i = 0; i < numberOfFiles; i++) {
String name = fileName[i];
boolean nameIsEmpty = (name == null) || name.equals("");
boolean isClass = (fileOptions[i] & 2) == 2 || nameIsEmpty;
if (isClass && nameIsEmpty) {
name = cpBands.getCpClass()[classBands.getClassThisInts()[classNum]] + ".class";
fileName[i] = name;
}
if (!overrideDeflateHint) {
fileDeflate[i] = (fileOptions[i] & 1) == 1 || options.shouldDeflate();
} else {
fileDeflate[i] = deflateHint;
}
fileIsClass[i] = isClass;
if (isClass) {
ClassFile classFile = buildClassFile(classNum);
classFile.write(dos);
dos.flush();
classFilesContents[classNum] = bos.toByteArray();
bos.reset();
classNum++;
}
}
}
/**
* Unpacks a packed stream (either .pack. or .pack.gz) into a corresponding
* JarOuputStream.
*
* @throws Pack200Exception
* if there is a problem unpacking
* @throws IOException
* if there is a problem with I/O during unpacking
*/
public void unpack(InputStream in, JarOutputStream out) throws IOException,
Pack200Exception {
unpackRead(in);
unpackProcess();
unpackWrite(out);
}
/*
* Package-private accessors for unpacking stages
*/
void unpackRead(InputStream in) throws IOException, Pack200Exception {
if (!in.markSupported())
in = new BufferedInputStream(in);
header = new SegmentHeader(this);
header.read(in);
int size = (int)header.getArchiveSize() - header.getArchiveSizeOffset();
if (doPreRead && header.getArchiveSize() != 0) {
byte[] data = new byte[size];
in.read(data);
internalBuffer = new BufferedInputStream(new ByteArrayInputStream(data));
} else {
readSegment(in);
}
}
void unpackProcess() throws IOException, Pack200Exception {
if(internalBuffer != null) {
readSegment(internalBuffer);
}
parseSegment();
}
void unpackWrite(JarOutputStream out) throws IOException, Pack200Exception {
writeJar(out);
}
/**
* Writes the segment to an output stream. The output stream should be
* pre-buffered for efficiency. Also takes the same input stream for
* reading, since the file bits may not be loaded and thus just copied from
* one stream to another. Doesn't close the output stream when finished, in
* case there are more entries (e.g. further segments) to be written.
*
* @param out
* the JarOutputStream to write data to
* @throws IOException
* if an error occurs while reading or writing to the streams
* @throws Pack200Exception
* if an error occurs while processing data
*/
public void writeJar(JarOutputStream out) throws IOException,
Pack200Exception {
String[] fileName = fileBands.getFileName();
int[] fileModtime = fileBands.getFileModtime();
long[] fileSize = fileBands.getFileSize();
byte[][] fileBits = fileBands.getFileBits();
// now write the files out
int classNum = 0;
int numberOfFiles = header.getNumberOfFiles();
long archiveModtime = header.getArchiveModtime();
for (int i = 0; i < numberOfFiles; i++) {
String name = fileName[i];
// For Pack200 archives, modtime is in seconds
// from the epoch. JarEntries need it to be in
// milliseconds from the epoch.
// Even though we're adding two longs and multiplying
// by 1000, we won't overflow because both longs are
// always under 2^32.
long modtime = 1000 * (archiveModtime + fileModtime[i]);
boolean deflate = fileDeflate[i];
JarEntry entry = new JarEntry(name);
if (deflate) {
entry.setMethod(ZipEntry.DEFLATED);
} else {
entry.setMethod(ZipEntry.STORED);
CRC32 crc = new CRC32();
if(fileIsClass[i]) {
crc.update(classFilesContents[classNum]);
entry.setSize(classFilesContents[classNum].length);
} else {
crc.update(fileBits[i]);
entry.setSize(fileSize[i]);
}
entry.setCrc(crc.getValue());
}
// On Windows at least, need to correct for timezone
entry.setTime(modtime - TimeZone.getDefault().getRawOffset());
out.putNextEntry(entry);
// write to output stream
if (fileIsClass[i]) {
entry.setSize(classFilesContents[classNum].length);
out.write(classFilesContents[classNum]);
classNum++;
} else {
entry.setSize(fileSize[i]);
out.write(fileBits[i]);
}
}
}
public SegmentConstantPool getConstantPool() {
return cpBands.getConstantPool();
}
public SegmentHeader getSegmentHeader() {
return header;
}
public void setPreRead(boolean value) {
doPreRead = value;
}
protected AttrDefinitionBands getAttrDefinitionBands() {
return attrDefinitionBands;
}
protected BcBands getBcBands() {
return bcBands;
}
protected ClassBands getClassBands() {
return classBands;
}
protected CpBands getCpBands() {
return cpBands;
}
protected FileBands getFileBands() {
return fileBands;
}
protected IcBands getIcBands() {
return icBands;
}
public void setLogLevel(int logLevel) {
this.logLevel = logLevel;
}
public void setLogStream(OutputStream logStream) {
this.logStream = new PrintWriter(logStream);
}
public void log(int logLevel, String message) {
if (this.logLevel >= logLevel) {
logStream.println(message);
}
}
/**
* Override the archive's deflate hint with the given boolean
*
* @param deflateHint -
* the deflate hint to use
*/
public void overrideDeflateHint(boolean deflateHint) {
this.overrideDeflateHint = true;
this.deflateHint = deflateHint;
}
}

@ -0,0 +1,317 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.util.List;
import org.apache.harmony.pack200.Pack200Exception;
import org.apache.harmony.unpack200.bytecode.ClassFileEntry;
import org.apache.harmony.unpack200.bytecode.ConstantPoolEntry;
/**
* SegmentConstantPool manages the constant pool used for re-creating class
* files.
*/
public class SegmentConstantPool {
private final CpBands bands;
private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache();
/**
* @param bands
*/
public SegmentConstantPool(CpBands bands) {
this.bands = bands;
}
// define in archive order
public static final int ALL = 0;
public static final int UTF_8 = 1;
public static final int CP_INT = 2;
public static final int CP_FLOAT = 3;
public static final int CP_LONG = 4;
public static final int CP_DOUBLE = 5;
public static final int CP_STRING = 6;
public static final int CP_CLASS = 7;
public static final int SIGNATURE = 8; // TODO and more to come --
public static final int CP_DESCR = 9;
public static final int CP_FIELD = 10;
public static final int CP_METHOD = 11;
public static final int CP_IMETHOD = 12;
protected static final String REGEX_MATCH_ALL = ".*";
protected static final String INITSTRING = "<init>";
protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*";
public ClassFileEntry getValue(int cp, long value) throws Pack200Exception {
int index = (int) value;
if (index == -1) {
return null;
} else if (index < 0) {
throw new Pack200Exception("Cannot have a negative range");
} else if (cp == UTF_8) {
return bands.cpUTF8Value(index);
} else if (cp == CP_INT) {
return bands.cpIntegerValue(index);
} else if (cp == CP_FLOAT) {
return bands.cpFloatValue(index);
} else if (cp == CP_LONG) {
return bands.cpLongValue(index);
} else if (cp == CP_DOUBLE) {
return bands.cpDoubleValue(index);
} else if (cp == CP_STRING) {
return bands.cpStringValue(index);
} else if (cp == CP_CLASS) {
return bands.cpClassValue(index);
} else if (cp == SIGNATURE) {
return bands.cpSignatureValue(index);
} else if (cp == CP_DESCR) {
return bands.cpNameAndTypeValue(index);
} else {
throw new Error("Tried to get a value I don't know about: " + cp);
}
}
/**
* Subset the constant pool of the specified type to be just that which has
* the specified class name. Answer the ConstantPoolEntry at the
* desiredIndex of the subsetted pool.
*
* @param cp
* type of constant pool array to search
* @param desiredIndex
* index of the constant pool
* @param desiredClassName
* class to use to generate a subset of the pool
* @return ConstantPoolEntry
* @throws Pack200Exception
*/
public ConstantPoolEntry getClassSpecificPoolEntry(int cp,
long desiredIndex, String desiredClassName) throws Pack200Exception {
int index = (int) desiredIndex;
int realIndex = -1;
String array[] = null;
if (cp == CP_FIELD) {
array = bands.getCpFieldClass();
} else if (cp == CP_METHOD) {
array = bands.getCpMethodClass();
} else if (cp == CP_IMETHOD) {
array = bands.getCpIMethodClass();
} else {
throw new Error("Don't know how to handle " + cp);
}
realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index);
return getConstantPoolEntry(cp, realIndex);
}
/**
* Given the name of a class, answer the CPClass associated with that class.
* Answer null if the class doesn't exist.
*
* @param name
* Class name to look for (form: java/lang/Object)
* @return CPClass for that class name, or null if not found.
*/
public ConstantPoolEntry getClassPoolEntry(String name) {
String classes[] = bands.getCpClass();
int index = matchSpecificPoolEntryIndex(classes, name, 0);
if (index == -1) {
return null;
}
try {
return getConstantPoolEntry(CP_CLASS, index);
} catch (Pack200Exception ex) {
throw new Error("Error getting class pool entry");
}
}
/**
* Answer the init method for the specified class.
*
* @param cp
* constant pool to search (must be CP_METHOD)
* @param value
* index of init method
* @param desiredClassName
* String class name of the init method
* @return CPMethod init method
* @throws Pack200Exception
*/
public ConstantPoolEntry getInitMethodPoolEntry(int cp, long value,
String desiredClassName) throws Pack200Exception {
int realIndex = -1;
String desiredRegex = REGEX_MATCH_INIT;
if (cp == CP_METHOD) {
realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(),
bands.getCpMethodDescriptor(), desiredClassName,
desiredRegex, (int) value);
} else {
throw new Error("Nothing but CP_METHOD can be an <init>");
}
return getConstantPoolEntry(cp, realIndex);
}
/**
* A number of things make use of subsets of structures. In one particular
* example, _super bytecodes will use a subset of method or field classes
* which have just those methods / fields defined in the superclass.
* Similarly, _this bytecodes use just those methods/fields defined in this
* class, and _init bytecodes use just those methods that start with <init>.
*
* This method takes an array of names, a String to match for, an index and
* a boolean as parameters, and answers the array position in the array of
* the indexth element which matches (or equals) the String (depending on
* the state of the boolean)
*
* In other words, if the class array consists of: Object [position 0, 0th
* instance of Object] String [position 1, 0th instance of String] String
* [position 2, 1st instance of String] Object [position 3, 1st instance of
* Object] Object [position 4, 2nd instance of Object] then
* matchSpecificPoolEntryIndex(..., "Object", 2, false) will answer 4.
* matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1.
*
* @param nameArray
* Array of Strings against which the compareString is tested
* @param compareString
* String for which to search
* @param desiredIndex
* nth element with that match (counting from 0)
* @return int index into nameArray, or -1 if not found.
*/
protected int matchSpecificPoolEntryIndex(String[] nameArray,
String compareString, int desiredIndex) {
return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString,
REGEX_MATCH_ALL, desiredIndex);
}
/**
* This method's function is to look through pairs of arrays. It keeps track
* of the number of hits it finds using the following basis of comparison
* for a hit: - the primaryArray[index] must be .equals() to the
* primaryCompareString - the secondaryArray[index] .matches() the
* secondaryCompareString. When the desiredIndex number of hits
* has been reached, the index into the original two arrays of
* the element hit is returned.
*
* @param primaryArray
* The first array to search
* @param secondaryArray
* The second array (must be same .length as primaryArray)
* @param primaryCompareString
* The String to compare against primaryArray using .equals()
* @param secondaryCompareRegex
* The String to compare against secondaryArray using .matches()
* @param desiredIndex
* The nth hit whose position we're seeking
* @return int index that represents the position of the nth hit in
* primaryArray and secondaryArray
*/
protected int matchSpecificPoolEntryIndex(String[] primaryArray,
String[] secondaryArray, String primaryCompareString,
String secondaryCompareRegex, int desiredIndex) {
int instanceCount = -1;
List indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString);
if(indexList.isEmpty()) {
// Primary key not found, no chance of finding secondary
return -1;
}
for(int index=0; index < indexList.size(); index++) {
int arrayIndex = ((Integer)indexList.get(index)).intValue();
if(regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) {
instanceCount++;
if(instanceCount == desiredIndex) {
return arrayIndex;
}
}
}
// We didn't return in the for loop, so the desiredMatch
// with desiredIndex must not exist in the arrays.
return -1;
}
/**
* We don't want a dependency on regex in Pack200. The only place one exists
* is in matchSpecificPoolEntryIndex(). To eliminate this dependency, we've
* implemented the world's stupidest regexMatch. It knows about the two
* forms we care about: .* (aka REGEX_MATCH_ALL) ^<init>.* (aka
* REGEX_MATCH_INIT) and will answer correctly if those are passed as the
* regexString.
*
* @param regexString
* String against which the compareString will be matched
* @param compareString
* String to match against the regexString
* @return boolean true if the compareString matches the regexString;
* otherwise false.
*/
protected static boolean regexMatches(String regexString,
String compareString) {
if (REGEX_MATCH_ALL.equals(regexString)) {
return true;
}
if (REGEX_MATCH_INIT.equals(regexString)) {
if (compareString.length() < (INITSTRING.length())) {
return false;
}
return (INITSTRING.equals(compareString.substring(0, INITSTRING
.length())));
}
throw new Error("regex trying to match a pattern I don't know: "
+ regexString);
}
public ConstantPoolEntry getConstantPoolEntry(int cp, long value)
throws Pack200Exception {
int index = (int) value;
if (index == -1) {
return null;
} else if (index < 0) {
throw new Pack200Exception("Cannot have a negative range");
} else if (cp == UTF_8) {
return bands.cpUTF8Value(index);
} else if (cp == CP_INT) {
return bands.cpIntegerValue(index);
} else if (cp == CP_FLOAT) {
return bands.cpFloatValue(index);
} else if (cp == CP_LONG) {
return bands.cpLongValue(index);
} else if (cp == CP_DOUBLE) {
return bands.cpDoubleValue(index);
} else if (cp == CP_STRING) {
return bands.cpStringValue(index);
} else if (cp == CP_CLASS) {
return bands.cpClassValue(index);
} else if (cp == SIGNATURE) {
throw new Error("I don't know what to do with signatures yet");
// return null /* new CPSignature(bands.getCpSignature()[index]) */;
} else if (cp == CP_DESCR) {
throw new Error("I don't know what to do with descriptors yet");
// return null /* new CPDescriptor(bands.getCpDescriptor()[index])
// */;
} else if (cp == CP_FIELD) {
return bands.cpFieldValue(index);
} else if (cp == CP_METHOD) {
return bands.cpMethodValue(index);
} else if (cp == CP_IMETHOD) {
return bands.cpIMethodValue(index);
} else {
// etc
throw new Error("Get value incomplete");
}
}
}

@ -0,0 +1,182 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
/**
* The SegmentConstantPool spends a lot of time searching
* through large arrays of Strings looking for matches.
* This can be sped up by caching the arrays in HashMaps
* so the String keys are looked up and resolve to positions
* in the array rather than iterating through the arrays
* each time.
*
* Because the arrays only grow (never shrink or change)
* we can use the last known size as a way to determine if
* the array has changed.
*
* Note that this cache must be synchronized externally
* if it is shared.
*/
public class SegmentConstantPoolArrayCache {
protected IdentityHashMap knownArrays = new IdentityHashMap(1000);
protected List lastIndexes;
protected String[] lastArray;
protected String lastKey;
/**
* Answer the indices for the given key in the given
* array. If no such key exists in the cached array,
* answer -1.
* @param array String[] array to search for the value
* @param key String value for which to search
* @return List collection of index positions in the array
*/
public List indexesForArrayKey(String[] array, String key) {
if(!arrayIsCached(array)) {
cacheArray(array);
}
// If the search is one we've just done, don't even
// bother looking and return the last indices. This
// is a second cache within the cache. This is
// efficient because we usually are looking for
// several secondary elements with the same primary
// key.
if((lastArray == array) && (lastKey == key)) {
return lastIndexes;
}
// Remember the last thing we found.
lastArray = array;
lastKey = key;
lastIndexes = ((CachedArray)knownArrays.get(array)).indexesForKey(key);
return lastIndexes;
}
/**
* Given a String array, answer true if the
* array is correctly cached. Answer false
* if the array is not cached, or if the
* array cache is outdated.
*
* @param array of String
* @return boolean true if up-to-date cache,
* otherwise false.
*/
protected boolean arrayIsCached(String[] array) {
if(!knownArrays.containsKey(array)) {
return false;
}
CachedArray cachedArray = (CachedArray)knownArrays.get(array);
if(cachedArray.lastKnownSize() != array.length) {
return false;
}
return true;
}
/**
* Cache the array passed in as the argument
* @param array String[] to cache
*/
protected void cacheArray(String[] array) {
if(arrayIsCached(array)) {
throw new IllegalArgumentException("Trying to cache an array that already exists");
}
knownArrays.put(array, new CachedArray(array));
// Invalidate the cache-within-a-cache
lastArray = null;
}
/**
* CachedArray keeps track of the last known size of
* an array as well as a HashMap that knows the mapping
* from element values to the indices of the array
* which contain that value.
*/
protected class CachedArray {
String[] primaryArray;
int lastKnownSize;
HashMap primaryTable;
public CachedArray(String[] array) {
super();
this.primaryArray = array;
this.lastKnownSize = array.length;
this.primaryTable = new HashMap(lastKnownSize);
cacheIndexes();
}
/**
* Answer the last known size of the array cached.
* If the last known size is not the same as the
* current size, the array must have changed.
* @return int last known size of the cached array
*/
public int lastKnownSize() {
return lastKnownSize;
}
/**
* Given a particular key, answer a List of
* index locations in the array which contain that
* key.
*
* If no elements are found, answer an empty list.
*
* @param key String element of the array
* @return List of indexes containing that key
* in the array.
*/
public List indexesForKey(String key) {
if(!primaryTable.containsKey(key)) {
return Collections.EMPTY_LIST;
}
return (List)primaryTable.get(key);
}
/**
* Given a primaryArray, cache its values in a HashMap
* to provide a backwards mapping from element values
* to element indexes. For instance, a primaryArray
* of:
* {"Zero", "Foo", "Two", "Foo"}
* would yield a HashMap of:
* "Zero" -> 0
* "Foo" -> 1, 3
* "Two" -> 2
* which is then cached.
*/
protected void cacheIndexes() {
for(int index=0; index < primaryArray.length; index++) {
String key = primaryArray[index];
if(!primaryTable.containsKey(key)) {
primaryTable.put(key, new ArrayList());
}
((ArrayList)primaryTable.get(key)).add(new Integer(index));
}
}
}
}

@ -0,0 +1,450 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.apache.harmony.pack200.BHSDCodec;
import org.apache.harmony.pack200.Codec;
import org.apache.harmony.pack200.Pack200Exception;
/**
* SegmentHeader is the header band of a {@link Segment}
*/
public class SegmentHeader {
private int archiveMajor;
private int archiveMinor;
private long archiveModtime;
private long archiveSize;
private int attributeDefinitionCount;
private InputStream bandHeadersInputStream;
private int bandHeadersSize;
private int classCount;
private int cpClassCount;
private int cpDescriptorCount;
private int cpDoubleCount;
private int cpFieldCount;
private int cpFloatCount;
private int cpIMethodCount;
private int cpIntCount;
private int cpLongCount;
private int cpMethodCount;
private int cpSignatureCount;
private int cpStringCount;
private int cpUTF8Count;
private int defaultClassMajorVersion;
private int defaultClassMinorVersion;
private int innerClassCount;
private int numberOfFiles;
private int segmentsRemaining;
private SegmentOptions options;
private final Segment segment;
/**
* The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they
* get their inspiration from ...
*/
private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
public SegmentHeader(Segment segment) {
this.segment = segment;
}
public int getArchiveSizeOffset() {
return archiveSizeOffset;
}
private int archiveSizeOffset;
public void read(InputStream in) throws IOException, Pack200Exception,
Error, Pack200Exception {
int word[] = decodeScalar("archive_magic_word", in, Codec.BYTE1,
magic.length);
for (int m = 0; m < magic.length; m++)
if (word[m] != magic[m])
throw new Error("Bad header");
setArchiveMinorVersion(decodeScalar("archive_minver", in,
Codec.UNSIGNED5));
setArchiveMajorVersion(decodeScalar("archive_majver", in,
Codec.UNSIGNED5));
options = new SegmentOptions(decodeScalar("archive_options", in,
Codec.UNSIGNED5));
parseArchiveFileCounts(in);
parseArchiveSpecialCounts(in);
parseCpCounts(in);
parseClassCounts(in);
if (getBandHeadersSize() > 0) {
byte[] bandHeaders = new byte[getBandHeadersSize()];
readFully(in, bandHeaders);
setBandHeadersData(bandHeaders);
}
archiveSizeOffset = archiveSizeOffset - in.available();
}
public void unpack() {
}
/**
* Sets the minor version of this archive
*
* @param version
* the minor version of the archive
* @throws Pack200Exception
* if the minor version is not 7
*/
private void setArchiveMinorVersion(int version) throws Pack200Exception {
if (version != 7)
throw new Pack200Exception("Invalid segment minor version");
archiveMinor = version;
}
/**
* Sets the major version of this archive.
*
* @param version
* the minor version of the archive
* @throws Pack200Exception
* if the major version is not 150
*/
private void setArchiveMajorVersion(int version) throws Pack200Exception {
if (version != 150)
throw new Pack200Exception("Invalid segment major version: "
+ version);
archiveMajor = version;
}
public long getArchiveModtime() {
return archiveModtime;
}
public int getArchiveMajor() {
return archiveMajor;
}
public int getArchiveMinor() {
return archiveMinor;
}
public int getAttributeDefinitionCount() {
return attributeDefinitionCount;
}
public int getClassCount() {
return classCount;
}
public int getCpClassCount() {
return cpClassCount;
}
public int getCpDescriptorCount() {
return cpDescriptorCount;
}
public int getCpDoubleCount() {
return cpDoubleCount;
}
public int getCpFieldCount() {
return cpFieldCount;
}
public int getCpFloatCount() {
return cpFloatCount;
}
public int getCpIMethodCount() {
return cpIMethodCount;
}
public int getCpIntCount() {
return cpIntCount;
}
public int getCpLongCount() {
return cpLongCount;
}
public int getCpMethodCount() {
return cpMethodCount;
}
public int getCpSignatureCount() {
return cpSignatureCount;
}
public int getCpStringCount() {
return cpStringCount;
}
public int getCpUTF8Count() {
return cpUTF8Count;
}
public int getDefaultClassMajorVersion() {
return defaultClassMajorVersion;
}
public int getDefaultClassMinorVersion() {
return defaultClassMinorVersion;
}
public int getInnerClassCount() {
return innerClassCount;
}
public void setNumberOfFiles(int numberOfFiles) {
this.numberOfFiles = numberOfFiles;
}
public long getArchiveSize() {
return archiveSize;
}
/**
* Obtain the band headers data as an input stream. If no band headers are
* present, this will return an empty input stream to prevent any further
* reads taking place.
*
* Note that as a stream, data consumed from this input stream can't be
* re-used. Data is only read from this stream if the encoding is such that
* additional information needs to be decoded from the stream itself.
*
* @return the band headers input stream
*/
public InputStream getBandHeadersInputStream() {
if (bandHeadersInputStream == null) {
bandHeadersInputStream = new ByteArrayInputStream(new byte[0]);
}
return bandHeadersInputStream;
}
public int getNumberOfFiles() {
return numberOfFiles;
}
public int getSegmentsRemaining() {
return segmentsRemaining;
}
public SegmentOptions getOptions() {
return options;
}
private void parseArchiveFileCounts(InputStream in) throws IOException,
Pack200Exception {
if (options.hasArchiveFileCounts()) {
setArchiveSize((long)decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32
| decodeScalar("archive_size_lo", in, Codec.UNSIGNED5));
archiveSizeOffset = in.available();
setSegmentsRemaining(decodeScalar("archive_next_count", in,
Codec.UNSIGNED5));
setArchiveModtime(decodeScalar("archive_modtime", in,
Codec.UNSIGNED5));
numberOfFiles = decodeScalar("file_count", in,
Codec.UNSIGNED5);
}
}
private void parseArchiveSpecialCounts(InputStream in) throws IOException,
Pack200Exception {
if (getOptions().hasSpecialFormats()) {
bandHeadersSize = decodeScalar("band_headers_size", in,
Codec.UNSIGNED5);
setAttributeDefinitionCount(decodeScalar("attr_definition_count",
in, Codec.UNSIGNED5));
}
}
private void parseClassCounts(InputStream in) throws IOException,
Pack200Exception {
innerClassCount = decodeScalar("ic_count", in, Codec.UNSIGNED5);
defaultClassMinorVersion = decodeScalar("default_class_minver",
in, Codec.UNSIGNED5);
defaultClassMajorVersion = decodeScalar("default_class_majver",
in, Codec.UNSIGNED5);
classCount = decodeScalar("class_count", in, Codec.UNSIGNED5);
}
private void parseCpCounts(InputStream in) throws IOException,
Pack200Exception {
cpUTF8Count = decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5);
if (getOptions().hasCPNumberCounts()) {
cpIntCount = decodeScalar("cp_Int_count", in, Codec.UNSIGNED5);
cpFloatCount = decodeScalar("cp_Float_count", in,
Codec.UNSIGNED5);
cpLongCount = decodeScalar("cp_Long_count", in,
Codec.UNSIGNED5);
cpDoubleCount = decodeScalar("cp_Double_count", in,
Codec.UNSIGNED5);
}
cpStringCount = decodeScalar("cp_String_count", in,
Codec.UNSIGNED5);
cpClassCount = decodeScalar("cp_Class_count", in, Codec.UNSIGNED5);
cpSignatureCount = decodeScalar("cp_Signature_count", in,
Codec.UNSIGNED5);
cpDescriptorCount = decodeScalar("cp_Descr_count", in,
Codec.UNSIGNED5);
cpFieldCount = decodeScalar("cp_Field_count", in, Codec.UNSIGNED5);
cpMethodCount = decodeScalar("cp_Method_count", in,
Codec.UNSIGNED5);
cpIMethodCount = decodeScalar("cp_Imethod_count", in,
Codec.UNSIGNED5);
}
/**
* Decode a number of scalars from the band file. A scalar is like a band,
* but does not perform any band code switching.
*
* @param name
* the name of the scalar (primarily for logging/debugging
* purposes)
* @param in
* the input stream to read from
* @param codec
* the codec for this scalar
* @return an array of decoded <code>long[]</code> values
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
private int[] decodeScalar(String name, InputStream in, BHSDCodec codec,
int n) throws IOException, Pack200Exception {
segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " (" + n
+ ")");
return codec.decodeInts(n, in);
}
/**
* Decode a scalar from the band file. A scalar is like a band, but does not
* perform any band code switching.
*
* @param name
* the name of the scalar (primarily for logging/debugging
* purposes)
* @param in
* the input stream to read from
* @param codec
* the codec for this scalar
* @return the decoded value
* @throws IOException
* if there is a problem reading from the underlying input
* stream
* @throws Pack200Exception
* if there is a problem decoding the value or that the value is
* invalid
*/
private int decodeScalar(String name, InputStream in, BHSDCodec codec)
throws IOException, Pack200Exception {
int ret = codec.decode(in);
segment
.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " as "
+ ret);
return ret;
}
public void setArchiveModtime(long archiveModtime) {
this.archiveModtime = archiveModtime;
}
public void setArchiveSize(long archiveSize) {
this.archiveSize = archiveSize;
}
private void setAttributeDefinitionCount(long valuie) {
this.attributeDefinitionCount = (int) valuie;
}
private void setBandHeadersData(byte[] bandHeaders) {
this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders);
}
public void setSegmentsRemaining(long value) {
segmentsRemaining = (int) value;
}
/**
* Completely reads in a byte array, akin to the implementation in
* {@link java.lang.DataInputStream}. TODO Refactor out into a separate
* InputStream handling class
*
* @param in
* the input stream to read from
* @param data
* the byte array to read into
* @throws IOException
* if a problem occurs during reading from the underlying stream
* @throws Pack200Exception
* if a problem occurs with an unexpected value or unsupported
* codec
*/
private void readFully(InputStream in, byte[] data) throws IOException,
Pack200Exception {
int total = in.read(data);
if (total == -1)
throw new EOFException("Failed to read any data from input stream");
while (total < data.length) {
int delta = in.read(data, total, data.length - total);
if (delta == -1)
throw new EOFException(
"Failed to read some data from input stream");
total += delta;
}
}
public int getBandHeadersSize() {
return bandHeadersSize;
}
}

@ -0,0 +1,126 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
import org.apache.harmony.pack200.Pack200Exception;
/**
* Stores the combinations of bit flags that can be used in the segment header
* options. Whilst this could be defined in {@link Segment}, it's cleaner to
* pull it out into a separate class, not least because methods can then be used
* to determine the semantic meaning of the flags. In languages with a
* pre-processor, these may be defined by macros that do bitflag manipulation
* instead.
*/
public class SegmentOptions {
private static final int DEFLATE_HINT = 1 << 5;
private static final int HAVE_ALL_CODE_FLAGS = 1 << 2;
private static final int HAVE_CLASS_FLAGS_HI = 1 << 9;
// private static final int UNUSED_3 = 2^3;
private static final int HAVE_CODE_FLAGS_HI = 1 << 10;
private static final int HAVE_CP_NUMBERS = 1 << 1;
private static final int HAVE_FIELD_FLAGS_HI = 1 << 10;
private static final int HAVE_FILE_HEADERS = 1 << 4;
private static final int HAVE_FILE_MODTIME = 1 << 6;
private static final int HAVE_FILE_OPTIONS = 1 << 7;
private static final int HAVE_FILE_SIZE_HI = 1 << 8;
private static final int HAVE_METHOD_FLAGS_HI = 1 << 11;
private static final int HAVE_SPECIAL_FORMATS = 1 << 0;
/**
* The bit flags that are defined as unused by the specification;
* specifically, every bit above bit 13 and bit 3.
*/
private static final int UNUSED = -1 << 13 | 1 << 3;
private int options;
/**
* Creates a new segment options with the given integer value.
*
* @param options
* the integer value to use as the flags
* @throws Pack200Exception
* if an unused bit (bit 3 or bit 13+) is non-zero
*/
public SegmentOptions(int options) throws Pack200Exception {
if ((options & UNUSED) != 0)
throw new Pack200Exception("Some unused flags are non-zero");
this.options = options;
}
public boolean hasAllCodeFlags() {
return (options & HAVE_ALL_CODE_FLAGS) != 0;
}
public boolean hasArchiveFileCounts() {
return (options & HAVE_FILE_HEADERS) != 0;
}
public boolean hasClassFlagsHi() {
return (options & HAVE_CLASS_FLAGS_HI) != 0;
}
public boolean hasCodeFlagsHi() {
return (options & HAVE_CODE_FLAGS_HI) != 0;
}
public boolean hasCPNumberCounts() {
return (options & HAVE_CP_NUMBERS) != 0;
}
public boolean hasFieldFlagsHi() {
return (options & HAVE_FIELD_FLAGS_HI) != 0;
}
public boolean hasFileModtime() {
return (options & HAVE_FILE_MODTIME) != 0;
}
public boolean hasFileOptions() {
return (options & HAVE_FILE_OPTIONS) != 0;
}
public boolean hasFileSizeHi() {
return (options & HAVE_FILE_SIZE_HI) != 0;
}
public boolean hasMethodFlagsHi() {
return (options & HAVE_METHOD_FLAGS_HI) != 0;
}
public boolean hasSpecialFormats() {
return (options & HAVE_SPECIAL_FORMATS) != 0;
}
public boolean shouldDeflate() {
return (options & DEFLATE_HINT) != 0;
}
}

@ -0,0 +1,164 @@
/*
* 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.
*/
package org.apache.harmony.unpack200;
/**
* Utility class for unpack200
*/
public final class SegmentUtils {
public static int countArgs(String descriptor) {
return countArgs(descriptor, 1);
}
public static int countInvokeInterfaceArgs(String descriptor) {
return countArgs(descriptor, 2);
}
/**
* Count the number of arguments in the descriptor. Each long or double
* counts as widthOfLongsAndDoubles; all other arguments count as 1.
*
* @param descriptor
* String for which arguments are counted
* @param widthOfLongsAndDoubles
* int increment to apply for longs doubles. This is typically 1
* when counting arguments alone, or 2 when counting arguments
* for invokeinterface.
* @return integer count
*/
protected static int countArgs(String descriptor, int widthOfLongsAndDoubles) {
int bra = descriptor.indexOf('(');
int ket = descriptor.indexOf(')');
if (bra == -1 || ket == -1 || ket < bra)
throw new IllegalArgumentException("No arguments");
boolean inType = false;
boolean consumingNextType = false;
int count = 0;
for (int i = bra + 1; i < ket; i++) {
char charAt = descriptor.charAt(i);
if (inType && charAt == ';') {
inType = false;
consumingNextType = false;
} else if (!inType && charAt == 'L') {
inType = true;
count++;
} else if (charAt == '[') {
consumingNextType = true;
} else if (inType) {
// NOP
} else {
if (consumingNextType) {
count++;
consumingNextType = false;
} else {
if (charAt == 'D' || charAt == 'J') {
count += widthOfLongsAndDoubles;
} else {
count++;
}
}
}
}
return count;
}
public static int countMatches(long[] flags, IMatcher matcher) {
int count = 0;
for (int i = 0; i < flags.length; i++) {
if (matcher.matches(flags[i]))
count++;
}
return count;
}
public static int countBit16(int[] flags) {
int count = 0;
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & (1 << 16)) != 0)
count++;
}
return count;
}
public static int countBit16(long[] flags) {
int count = 0;
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & (1 << 16)) != 0)
count++;
}
return count;
}
public static int countBit16(long[][] flags) {
int count = 0;
for (int i = 0; i < flags.length; i++) {
for (int j = 0; j < flags[i].length; j++) {
if ((flags[i][j] & (1 << 16)) != 0)
count++;
}
}
return count;
}
public static int countMatches(long[][] flags, IMatcher matcher) {
int count = 0;
for (int i = 0; i < flags.length; i++) {
count += countMatches(flags[i], matcher);
}
return count;
}
/**
* Answer the index of the first character <= '$' in the parameter. This is
* used instead of indexOf('$') because inner classes may be separated by
* any character <= '$' (in other words, Foo#Bar is as valid as Foo$Bar). If
* no $ character is found, answer -1.
*
* @param string
* String to search for $
* @return first index of $ character, or -1 if not found
*/
public static int indexOfFirstDollar(String string) {
for (int index = 0; index < string.length(); index++) {
if (string.charAt(index) <= '$') {
return index;
}
}
return -1;
}
private SegmentUtils() {
// Intended to be a helper class
}
/**
* This is a debugging message to aid the developer in writing this class.
* If the property 'debug.unpack200' is set, this will generate messages to
* stderr; otherwise, it will be silent.
*
* @param message
* @deprecated this may be removed from production code
*/
public static void debug(String message) {
if (System.getProperty("debug.unpack200") != null) {
System.err.println(message);
}
}
}

@ -0,0 +1,73 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* AnnotationDefault class file attribute
*/
public class AnnotationDefaultAttribute extends AnnotationsAttribute {
private final ElementValue element_value;
private static CPUTF8 attributeName;
public static void setAttributeName(CPUTF8 cpUTF8Value) {
attributeName = cpUTF8Value;
}
public AnnotationDefaultAttribute(ElementValue element_value) {
super(attributeName);
this.element_value = element_value;
}
protected int getLength() {
return element_value.getLength();
}
protected void writeBody(DataOutputStream dos) throws IOException {
element_value.writeBody(dos);
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
element_value.resolve(pool);
}
public String toString() {
return "AnnotationDefault: " + element_value;
}
public boolean equals(Object obj) {
return this == obj;
}
protected ClassFileEntry[] getNestedClassFileEntries() {
List nested = new ArrayList();
nested.add(attributeName);
nested.addAll(element_value.getClassFileEntries());
ClassFileEntry[] nestedEntries = new ClassFileEntry[nested.size()];
for (int i = 0; i < nestedEntries.length; i++) {
nestedEntries[i] = (ClassFileEntry) nested.get(i);
}
return nestedEntries;
}
}

@ -0,0 +1,190 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract superclass for Annotations attributes
*/
public abstract class AnnotationsAttribute extends Attribute {
/**
* Class to represent the annotation structure for class file attributes
*/
public static class Annotation {
private final int num_pairs;
private final CPUTF8[] element_names;
private final ElementValue[] element_values;
private final CPUTF8 type;
// Resolved values
private int type_index;
private int[] name_indexes;
public Annotation(int num_pairs, CPUTF8 type, CPUTF8[] element_names,
ElementValue[] element_values) {
this.num_pairs = num_pairs;
this.type = type;
this.element_names = element_names;
this.element_values = element_values;
}
public int getLength() {
int length = 4;
for (int i = 0; i < num_pairs; i++) {
length += 2;
length += element_values[i].getLength();
}
return length;
}
public void resolve(ClassConstantPool pool) {
type.resolve(pool);
type_index = pool.indexOf(type);
name_indexes = new int[num_pairs];
for (int i = 0; i < element_names.length; i++) {
element_names[i].resolve(pool);
name_indexes[i] = pool.indexOf(element_names[i]);
element_values[i].resolve(pool);
}
}
public void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(type_index);
dos.writeShort(num_pairs);
for (int i = 0; i < num_pairs; i++) {
dos.writeShort(name_indexes[i]);
element_values[i].writeBody(dos);
}
}
public List getClassFileEntries() {
List entries = new ArrayList();
for (int i = 0; i < element_names.length; i++) {
entries.add(element_names[i]);
entries.addAll(element_values[i].getClassFileEntries());
}
entries.add(type);
return entries;
}
}
public static class ElementValue {
private final Object value;
private final int tag;
// resolved value index if it's a constant
private int constant_value_index = -1;
public ElementValue(int tag, Object value) {
this.tag = tag;
this.value = value;
}
public List getClassFileEntries() {
List entries = new ArrayList(1);
if(value instanceof CPNameAndType) {
// used to represent enum, so don't include the actual CPNameAndType
entries.add(((CPNameAndType)value).name);
entries.add(((CPNameAndType)value).descriptor);
} else if(value instanceof ClassFileEntry) {
entries.add(value);
} else if (value instanceof ElementValue[]) {
ElementValue[] values = (ElementValue[]) value;
for (int i = 0; i < values.length; i++) {
entries.addAll(values[i].getClassFileEntries());
}
} else if (value instanceof Annotation) {
entries.addAll(((Annotation)value).getClassFileEntries());
}
return entries;
}
public void resolve(ClassConstantPool pool) {
if (value instanceof CPConstant) {
((CPConstant) value).resolve(pool);
constant_value_index = pool.indexOf((CPConstant) value);
} else if (value instanceof CPClass) {
((CPClass) value).resolve(pool);
constant_value_index = pool.indexOf((CPClass) value);
} else if (value instanceof CPNameAndType) {
((CPNameAndType) value).resolve(pool);
} else if (value instanceof Annotation) {
((Annotation) value).resolve(pool);
} else if (value instanceof ElementValue[]) {
ElementValue[] nestedValues = (ElementValue[]) value;
for (int i = 0; i < nestedValues.length; i++) {
nestedValues[i].resolve(pool);
}
}
}
public void writeBody(DataOutputStream dos) throws IOException {
dos.writeByte(tag);
if (constant_value_index != -1) {
dos.writeShort(constant_value_index);
} else if (value instanceof CPNameAndType) {
((CPNameAndType) value).writeBody(dos);
} else if (value instanceof Annotation) {
((Annotation) value).writeBody(dos);
} else if (value instanceof ElementValue[]) {
ElementValue[] nestedValues = (ElementValue[]) value;
for (int i = 0; i < nestedValues.length; i++) {
nestedValues[i].writeBody(dos);
}
}
}
public int getLength() {
switch (tag) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'c':
return 3;
case 'e':
return 5;
case '[':
int length = 3;
ElementValue[] nestedValues = (ElementValue[]) value;
for (int i = 0; i < nestedValues.length; i++) {
length += nestedValues[i].getLength();
}
return length;
case '@':
return (1 + ((Annotation) value).getLength());
}
return 0;
}
}
public AnnotationsAttribute(CPUTF8 attributeName) {
super(attributeName);
}
}

@ -0,0 +1,115 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Abstract superclass for class file attributes
*/
public abstract class Attribute extends ClassFileEntry {
protected final CPUTF8 attributeName;
private int attributeNameIndex;
public Attribute(CPUTF8 attributeName) {
this.attributeName = attributeName;
}
protected void doWrite(DataOutputStream dos) throws IOException {
dos.writeShort(attributeNameIndex);
dos.writeInt(getLength());
writeBody(dos);
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (this.getClass() != obj.getClass())
return false;
final Attribute other = (Attribute) obj;
if (attributeName == null) {
if (other.attributeName != null)
return false;
} else if (!attributeName.equals(other.attributeName))
return false;
return true;
}
protected CPUTF8 getAttributeName() {
return attributeName;
}
protected abstract int getLength();
/**
* Answer the length of the receiver including its header (the u2 for the
* attribute name and the u4 for the attribute length). This is relevant
* when attributes are nested within other attributes - the outer attribute
* needs to take the inner attribute headers into account when calculating
* its length.
*
* @return int adjusted length
*/
protected int getLengthIncludingHeader() {
return getLength() + 2 + 4;
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { getAttributeName() };
}
/**
* Answer true if the receiver needs to have BCI renumbering applied to it;
* otherwise answer false.
*
* @return boolean BCI renumbering required
*/
public boolean hasBCIRenumbering() {
return false;
}
/**
* Answer true if the receiver is a source file attribute (which gets
* special handling when the class is built); otherwise answer false.
*
* @return boolean source file attribute
*/
public boolean isSourceFileAttribute() {
return false;
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result
+ ((attributeName == null) ? 0 : attributeName.hashCode());
return result;
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
attributeNameIndex = pool.indexOf(attributeName);
}
protected abstract void writeBody(DataOutputStream dos) throws IOException;
}

@ -0,0 +1,78 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.harmony.pack200.Pack200Exception;
/**
* Abstract superclass for attributes that have some part encoded with a BCI
* renumbering
*/
public abstract class BCIRenumberedAttribute extends Attribute {
protected boolean renumbered;
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.bytecode.Attribute#hasBCIRenumbering()
*/
public boolean hasBCIRenumbering() {
return true;
}
public BCIRenumberedAttribute(CPUTF8 attributeName) {
super(attributeName);
}
protected abstract int getLength();
protected abstract void writeBody(DataOutputStream dos) throws IOException;
public abstract String toString();
protected abstract int[] getStartPCs();
/**
* In Pack200, line number tables are BCI renumbered. This method takes the
* byteCodeOffsets (which is a List of Integers specifying the offset in the
* byte code array of each instruction) and updates the start_pcs so that it
* points to the instruction index itself, not the BCI renumbering of the
* instruction.
*
* @param byteCodeOffsets
* List of Integer offsets of the bytecode array
* @throws Pack200Exception
*/
public void renumber(List byteCodeOffsets) throws Pack200Exception {
if (renumbered) {
throw new Error(
"Trying to renumber a line number table that has already been renumbered");
}
renumbered = true;
int[] startPCs = getStartPCs();
for (int index = 0; index < startPCs.length; index++) {
startPCs[index] = ((Integer) byteCodeOffsets.get(startPCs[index]))
.intValue();
}
}
}

@ -0,0 +1,383 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.harmony.unpack200.Segment;
import org.apache.harmony.unpack200.bytecode.forms.ByteCodeForm;
/**
* A bytecode class file entry.
*/
public class ByteCode extends ClassFileEntry {
public static ByteCode getByteCode(int opcode) {
int byteOpcode = 0xFF & opcode;
if(ByteCodeForm.get(byteOpcode).hasNoOperand()) {
if(null == noArgByteCodes[byteOpcode]) {
noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode);
}
return noArgByteCodes[byteOpcode];
}
return new ByteCode(byteOpcode);
}
private static ByteCode[] noArgByteCodes = new ByteCode[255];
private final ByteCodeForm byteCodeForm;
private ClassFileEntry[] nested;
private int[][] nestedPositions;
private int[] rewrite;
private int byteCodeOffset = -1;
private int[] byteCodeTargets;
protected ByteCode(int opcode) {
this(opcode, ClassFileEntry.NONE);
}
protected ByteCode(int opcode, ClassFileEntry[] nested) {
this.byteCodeForm = ByteCodeForm.get(opcode);
this.rewrite = byteCodeForm.getRewriteCopy();
this.nested = nested;
}
protected void doWrite(DataOutputStream dos) throws IOException {
for (int i = 0; i < rewrite.length; i++) {
dos.writeByte(rewrite[i]);
}
}
public boolean equals(Object obj) {
return this == obj;
}
public void extractOperands(OperandManager operandManager, Segment segment,
int codeLength) {
// Given an OperandTable, figure out which operands
// the receiver needs and stuff them in operands.
// Later on the operands can be rewritten (But that's
// later, not now).
ByteCodeForm currentByteCodeForm = getByteCodeForm();
currentByteCodeForm.setByteCodeOperands(this, operandManager,
codeLength);
}
protected ByteCodeForm getByteCodeForm() {
return byteCodeForm;
}
public int getLength() {
return rewrite.length;
}
public String getName() {
return getByteCodeForm().getName();
}
public ClassFileEntry[] getNestedClassFileEntries() {
return nested;
}
public int getOpcode() {
return getByteCodeForm().getOpcode();
}
public int getOperandType() {
return getByteCodeForm().getOperandType();
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
cachedHashCode = objectHashCode();
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
/*
* (non-Javadoc)
*
* @see org.apache.harmony.unpack200.bytecode.ClassFileEntry#resolve(org.apache.harmony.unpack200.bytecode.ClassConstantPool)
*/
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
if (nested.length > 0) {
// Update the bytecode rewrite array so that it points
// to the elements of the nested array.
for (int index = 0; index < nested.length; index++) {
int argLength = getNestedPosition(index)[1];
switch (argLength) {
case 1:
setOperandByte(pool.indexOf(nested[index]),
getNestedPosition(index)[0]);
break;
case 2:
setOperand2Bytes(pool.indexOf(nested[index]),
getNestedPosition(index)[0]);
break;
default:
throw new Error("Unhandled resolve " + this);
}
}
}
}
/**
* Given an array of ints which correspond to bytes in the operand of the
* bytecode, set the rewrite bytes of the operand to be the appropriate
* values. All values in operands[] will be masked with 0xFF so they fit
* into a byte.
*
* @param operands
* int[] rewrite operand bytes
*/
public void setOperandBytes(int[] operands) {
int firstOperandIndex = getByteCodeForm().firstOperandIndex();
int byteCodeFormLength = getByteCodeForm().operandLength();
if (firstOperandIndex < 1) {
// No operand rewriting permitted for this bytecode
throw new Error("Trying to rewrite " + this
+ " that has no rewrite");
}
if (byteCodeFormLength != operands.length) {
throw new Error("Trying to rewrite " + this + " with "
+ operands.length + " but bytecode has length "
+ byteCodeForm.operandLength());
}
for (int index = 0; index < byteCodeFormLength; index++) {
rewrite[index + firstOperandIndex] = operands[index] & 0xFF;
}
}
/**
* Given an int operand, set the rewrite bytes for that position and the one
* immediately following it to a high-byte, low-byte encoding of the
* operand.
*
* @param operand
* int to set the rewrite bytes to
* @param position
* int position in the operands of the rewrite bytes. For a
* rewrite array of {100, -1, -1, -1} position 0 is the first -1,
* position 1 is the second -1, etc.
*/
public void setOperand2Bytes(int operand, int position) {
int firstOperandIndex = getByteCodeForm().firstOperandIndex();
int byteCodeFormLength = getByteCodeForm().getRewrite().length;
if (firstOperandIndex < 1) {
// No operand rewriting permitted for this bytecode
throw new Error("Trying to rewrite " + this
+ " that has no rewrite");
}
if (firstOperandIndex + position + 1 > byteCodeFormLength) {
throw new Error("Trying to rewrite " + this
+ " with an int at position " + position
+ " but this won't fit in the rewrite array");
}
rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8;
rewrite[firstOperandIndex + position + 1] = operand & 0xFF;
}
/**
* This is just like setOperandInt, but takes special care when the operand
* is less than 0 to make sure it's written correctly.
*
* @param operand
* int to set the rewrite bytes to
* @param position
* int position of the operands in the rewrite bytes
*/
public void setOperandSigned2Bytes(int operand, int position) {
if (operand >= 0) {
setOperand2Bytes(operand, position);
} else {
int twosComplementOperand = 0x10000 + operand;
setOperand2Bytes(twosComplementOperand, position);
}
}
/**
* Given an int operand, treat it as a byte and set the rewrite byte for
* that position to that value. Mask of anything beyond 0xFF.
*
* @param operand
* int to set the rewrite byte to (unsigned)
* @param position
* int position in the operands of the rewrite bytes. For a
* rewrite array of {100, -1, -1, -1} position 0 is the first -1,
* position 1 is the second -1, etc.
*/
public void setOperandByte(int operand, int position) {
int firstOperandIndex = getByteCodeForm().firstOperandIndex();
int byteCodeFormLength = getByteCodeForm().operandLength();
if (firstOperandIndex < 1) {
// No operand rewriting permitted for this bytecode
throw new Error("Trying to rewrite " + this
+ " that has no rewrite");
}
if (firstOperandIndex + position > byteCodeFormLength) {
throw new Error("Trying to rewrite " + this
+ " with an byte at position " + position
+ " but this won't fit in the rewrite array");
}
rewrite[firstOperandIndex + position] = operand & 0xFF;
}
public String toString() {
return getByteCodeForm().getName();
}
public void setNested(ClassFileEntry[] nested) {
this.nested = nested;
}
/**
* nestedPositions is an array of arrays of ints. Each subarray specifies a
* position of a nested element (from the nested[] array) and the length of
* that element.
*
* For instance, one might have a nested of: {CPClass java/lang/Foo, CPFloat
* 3.14} The nestedPositions would then be: {{0,2},{2,2}} In other words,
* when the bytecode is resolved, the CPClass will be resolved to an int and
* inserted at position 0 and 1 of the rewrite arguments (the first
* occurrences of -1). The CPFloat will be resolved to an int position and
* inserted at positions 2 and 3 of the rewrite arguments.
*
* @param nestedPositions
*/
public void setNestedPositions(int[][] nestedPositions) {
this.nestedPositions = nestedPositions;
}
public int[][] getNestedPositions() {
return nestedPositions;
}
public int[] getNestedPosition(int index) {
return getNestedPositions()[index];
}
/**
* This method will answer true if the receiver is a multi-bytecode
* instruction (such as aload0_putfield_super); otherwise, it will answer
* false.
*
* @return boolean true if multibytecode, false otherwise
*/
public boolean hasMultipleByteCodes() {
return getByteCodeForm().hasMultipleByteCodes();
}
/**
* ByteCodes may need to know their position in the code array (in
* particular, label byte codes need to know where they are in order to
* calculate their targets). This method lets the CodeAttribute specify
* where the byte code is.
*
* Since there are no aload0+label instructions, this method doesn't worry
* about multioperation bytecodes.
*
* @param byteCodeOffset
* int position in code array.
*/
public void setByteCodeIndex(int byteCodeOffset) {
this.byteCodeOffset = byteCodeOffset;
}
public int getByteCodeIndex() {
return byteCodeOffset;
}
/**
* Some ByteCodes (in particular, those with labels) have to keep track of
* byteCodeTargets. These are initially offsets in the CodeAttribute array
* relative to the byteCodeOffset, but later get fixed up to point to the
* absolute position in the CodeAttribute array. This method sets the
* targets.
*
* @param byteCodeTargets
* int index in array
*/
public void setByteCodeTargets(int[] byteCodeTargets) {
this.byteCodeTargets = byteCodeTargets;
}
public int[] getByteCodeTargets() {
return byteCodeTargets;
}
/**
* Some ByteCodes (in particular, those with labels need to be fixed up
* after all the bytecodes in the CodeAttribute have been added. (This can't
* be done beforehand because the CodeAttribute needs to be complete before
* targets can be assigned.)
*
* @param codeAttribute
* the code attribute
*/
public void applyByteCodeTargetFixup(CodeAttribute codeAttribute) {
getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
}
/**
* Some bytecodes (the ones with variable lengths) can't have a static
* rewrite array - they need the ability to update the array. This method
* permits that.
*
* Note that this should not be called from bytecodes which have a static
* rewrite; use the table in ByteCodeForm instead to specify those rewrites.
*
* @param rewrite
*/
public void setRewrite(int[] rewrite) {
this.rewrite = rewrite;
}
/**
* Some bytecodes (the ones with variable lengths) can't have a static
* rewrite array - they need the ability to update the array. This method
* permits their associated bytecode formst to query their rewrite array.
*
* Note that this should not be called from bytecodes which have a static
* rewrite; use the table in ByteCodeForm instead to specify those rewrites.
*/
public int[] getRewrite() {
return rewrite;
}
public boolean nestedMustStartClassPool() {
return byteCodeForm.nestedMustStartClassPool();
}
}

@ -0,0 +1,96 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Constant pool entry for a class
*/
public class CPClass extends ConstantPoolEntry {
private int index;
public String name;
private final CPUTF8 utf8;
/**
* Creates a new CPClass
*
* @param name
* @param globalIndex
* index in CpBands
* @throws NullPointerException
* if name is null
*/
public CPClass(CPUTF8 name, int globalIndex) {
super(ConstantPoolEntry.CP_Class, globalIndex);
if (name == null) {
throw new NullPointerException("Null arguments are not allowed");
}
this.name = name.underlyingString();
this.utf8 = name;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (this.getClass() != obj.getClass())
return false;
final CPClass other = (CPClass) obj;
return utf8.equals(other.utf8);
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { utf8, };
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
cachedHashCode = utf8.hashCode();
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
index = pool.indexOf(utf8);
}
public String toString() {
return "Class: " + getName();
}
public String getName() {
return name;
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(index);
}
}

@ -0,0 +1,71 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
/**
* Abstract superclass for constant pool constant entries such as numbers or
* Strings
*/
public abstract class CPConstant extends ConstantPoolEntry {
private final Object value;
/**
* Create a new CPConstant
*
* @param tag
* @param value
* @param globalIndex
* index in CpBands
* @throws NullPointerException
* if value is null
*/
public CPConstant(byte tag, Object value, int globalIndex) {
super(tag, globalIndex);
this.value = value;
if (value == null) {
throw new NullPointerException("Null arguments are not allowed");
}
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (this.getClass() != obj.getClass())
return false;
final CPConstant other = (CPConstant) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((value == null) ? 0 : value.hashCode());
return result;
}
protected Object getValue() {
return value;
}
}

@ -0,0 +1,32 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
/**
* Abstract superclass for constant pool entries that are numbers.
*/
public abstract class CPConstantNumber extends CPConstant {
public CPConstantNumber(byte tag, Object value, int globalIndex) {
super(tag, value, globalIndex);
}
protected Number getNumber() {
return (Number) getValue();
}
}

@ -0,0 +1,39 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Double constant pool entry.
*/
public class CPDouble extends CPConstantNumber {
public CPDouble(Double value, int globalIndex) {
super(ConstantPoolEntry.CP_Double, value, globalIndex);
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeDouble(getNumber().doubleValue());
}
public String toString() {
return "Double: " + getValue();
}
}

@ -0,0 +1,34 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.util.List;
/**
* Field constant pool entry.
*/
public class CPField extends CPMember {
public CPField(CPUTF8 name, CPUTF8 descriptor, long flags, List attributes) {
super(name, descriptor, flags, attributes);
}
public String toString() {
return "Field: " + name + "(" + descriptor + ")";
}
}

@ -0,0 +1,98 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Field reference constant pool entry.
*/
public class CPFieldRef extends ConstantPoolEntry {
CPClass className;
transient int classNameIndex;
private final CPNameAndType nameAndType;
transient int nameAndTypeIndex;
public CPFieldRef(CPClass className, CPNameAndType descriptor, int globalIndex) {
super(ConstantPoolEntry.CP_Fieldref, globalIndex);
this.className = className;
this.nameAndType = descriptor;
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { className, nameAndType };
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
nameAndTypeIndex = pool.indexOf(nameAndType);
classNameIndex = pool.indexOf(className);
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(classNameIndex);
dos.writeShort(nameAndTypeIndex);
}
public String toString() {
return "FieldRef: " + className + "#" + nameAndType;
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result
+ ((className == null) ? 0 : className.hashCode());
result = PRIME * result
+ ((nameAndType == null) ? 0 : nameAndType.hashCode());
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CPFieldRef other = (CPFieldRef) obj;
if (className == null) {
if (other.className != null)
return false;
} else if (!className.equals(other.className))
return false;
if (nameAndType == null) {
if (other.nameAndType != null)
return false;
} else if (!nameAndType.equals(other.nameAndType))
return false;
return true;
}
}

@ -0,0 +1,39 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Float constant pool entry.
*/
public class CPFloat extends CPConstantNumber {
public CPFloat(Float value, int globalIndex) {
super(ConstantPoolEntry.CP_Float, value, globalIndex);
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeFloat(getNumber().floatValue());
}
public String toString() {
return "Float: " + getValue();
}
}

@ -0,0 +1,39 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Integer constant pool entry.
*/
public class CPInteger extends CPConstantNumber {
public CPInteger(Integer value, int globalIndex) {
super(ConstantPoolEntry.CP_Integer, value, globalIndex);
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeInt(getNumber().intValue());
}
public String toString() {
return "Integer: " + getValue();
}
}

@ -0,0 +1,54 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
/**
* Interface method reference constant pool entry.
*/
public class CPInterfaceMethodRef extends CPRef {
public CPInterfaceMethodRef(CPClass className, CPNameAndType descriptor, int globalIndex) {
super(ConstantPoolEntry.CP_InterfaceMethodref, className, descriptor, globalIndex);
}
/**
* This method answers the value this method will use for an invokeinterface
* call. This is equal to 1 + the count of all the args, where longs and
* doubles count for 2 and all others count for 1.
*
* @return integer count
*/
public int invokeInterfaceCount() {
return nameAndType.invokeInterfaceCount();
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result + className.hashCode();
result = PRIME * result + nameAndType.hashCode();
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
}

@ -0,0 +1,39 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Long constant pool entry.
*/
public class CPLong extends CPConstantNumber {
public CPLong(Long value, int globalIndex) {
super(ConstantPoolEntry.CP_Long, value, globalIndex);
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeLong(getNumber().longValue());
}
public String toString() {
return "Long: " + getValue();
}
}

@ -0,0 +1,123 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* Superclass for member constant pool entries, such as fields or methods.
*/
public class CPMember extends ClassFileEntry {
List attributes;
short flags;
CPUTF8 name;
transient int nameIndex;
protected final CPUTF8 descriptor;
transient int descriptorIndex;
/**
* Create a new CPMember
*
* @param name
* @param descriptor
* @param flags
* @param attributes
* @throws NullPointerException
* if name or descriptor is null
*/
public CPMember(CPUTF8 name, CPUTF8 descriptor, long flags, List attributes) {
this.name = name;
this.descriptor = descriptor;
this.flags = (short) flags;
this.attributes = (attributes == null ? Collections.EMPTY_LIST
: attributes);
if (name == null || descriptor == null) {
throw new NullPointerException("Null arguments are not allowed");
}
}
protected ClassFileEntry[] getNestedClassFileEntries() {
int attributeCount = attributes.size();
ClassFileEntry[] entries = new ClassFileEntry[attributeCount + 2];
entries[0] = name;
entries[1] = descriptor;
for (int i = 0; i < attributeCount; i++) {
entries[i + 2] = (Attribute) attributes.get(i);
}
return entries;
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
nameIndex = pool.indexOf(name);
descriptorIndex = pool.indexOf(descriptor);
for(int it = 0; it < attributes.size(); it++) {
Attribute attribute = (Attribute) attributes.get(it);
attribute.resolve(pool);
}
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + attributes.hashCode();
result = PRIME * result + descriptor.hashCode();
result = PRIME * result + flags;
result = PRIME * result + name.hashCode();
return result;
}
public String toString() {
return "CPMember: " + name + "(" + descriptor + ")";
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CPMember other = (CPMember) obj;
if (!attributes.equals(other.attributes))
return false;
if (!descriptor.equals(other.descriptor))
return false;
if (flags != other.flags)
return false;
if (!name.equals(other.name))
return false;
return true;
}
protected void doWrite(DataOutputStream dos) throws IOException {
dos.writeShort(flags);
dos.writeShort(nameIndex);
dos.writeShort(descriptorIndex);
int attributeCount = attributes.size();
dos.writeShort(attributeCount);
for (int i = 0; i < attributeCount; i++) {
Attribute attribute = (Attribute) attributes.get(i);
attribute.doWrite(dos);
}
}
}

@ -0,0 +1,52 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.util.List;
/**
* Method constant pool entry.
*/
public class CPMethod extends CPMember {
public CPMethod(CPUTF8 name, CPUTF8 descriptor, long flags, List attributes) {
super(name, descriptor, flags, attributes);
}
public String toString() {
return "Method: " + name + "(" + descriptor + ")";
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result + name.hashCode();
result = PRIME * result + descriptor.hashCode();
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
}

@ -0,0 +1,51 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
/**
* Method reference constant pool entry.
*/
public class CPMethodRef extends CPRef {
public CPMethodRef(CPClass className, CPNameAndType descriptor, int globalIndex) {
super(ConstantPoolEntry.CP_Methodref, className, descriptor, globalIndex);
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { className, nameAndType };
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result + className.hashCode();
result = PRIME * result + nameAndType.hashCode();
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
}

@ -0,0 +1,123 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.harmony.unpack200.SegmentUtils;
/**
* Name and Type pair constant pool entry.
*/
public class CPNameAndType extends ConstantPoolEntry {
CPUTF8 descriptor;
transient int descriptorIndex;
CPUTF8 name;
transient int nameIndex;
/**
* Create a new CPNameAndType
*
* @param name
* @param descriptor
* @param globalIndex - index in CpBands
* @throws NullPointerException
* if name or descriptor is null
*/
public CPNameAndType(CPUTF8 name, CPUTF8 descriptor, int globalIndex) {
super(ConstantPoolEntry.CP_NameAndType, globalIndex);
this.name = name;
this.descriptor = descriptor;
if (name == null || descriptor == null) {
throw new NullPointerException("Null arguments are not allowed");
}
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { name, descriptor };
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
descriptorIndex = pool.indexOf(descriptor);
nameIndex = pool.indexOf(name);
}
/*
* field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2
* attributes_count; attribute_info attributes[attributes_count]; }
*/
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(nameIndex);
dos.writeShort(descriptorIndex);
}
public String toString() {
return "NameAndType: " + name + "(" + descriptor + ")";
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result + descriptor.hashCode();
result = PRIME * result + name.hashCode();
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CPNameAndType other = (CPNameAndType) obj;
if (!descriptor.equals(other.descriptor))
return false;
if (!name.equals(other.name))
return false;
return true;
}
/**
* Answers the invokeinterface count argument when the receiver is treated
* as an invokeinterface target. This value is not meaningful if the
* receiver is not an invokeinterface target.
*
* @return count
*/
public int invokeInterfaceCount() {
return 1 + SegmentUtils.countInvokeInterfaceArgs(descriptor
.underlyingString());
}
}

@ -0,0 +1,114 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Abstract superclass for reference constant pool entries, such as a method or
* field reference.
*/
public abstract class CPRef extends ConstantPoolEntry {
CPClass className;
transient int classNameIndex;
protected CPNameAndType nameAndType;
transient int nameAndTypeIndex;
/**
* Create a new CPRef
*
* @param type
* @param className
* @param descriptor
* @param globalIndex
* index in CpBands
* @throws NullPointerException
* if descriptor or className is null
*/
public CPRef(byte type, CPClass className, CPNameAndType descriptor, int globalIndex) {
super(type, globalIndex);
this.className = className;
this.nameAndType = descriptor;
if (descriptor == null || className == null) {
throw new NullPointerException("Null arguments are not allowed");
}
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
if (this.hashCode() != obj.hashCode()) {
return false;
}
final CPRef other = (CPRef) obj;
if (!className.equals(other.className))
return false;
if (!nameAndType.equals(other.nameAndType))
return false;
return true;
}
protected ClassFileEntry[] getNestedClassFileEntries() {
ClassFileEntry[] entries = new ClassFileEntry[2];
entries[0] = className;
entries[1] = nameAndType;
return entries;
}
public int hashCode() {
final int PRIME = 37;
return (PRIME * className.hashCode()) + nameAndType.hashCode();
}
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
nameAndTypeIndex = pool.indexOf(nameAndType);
classNameIndex = pool.indexOf(className);
}
protected String cachedToString;
public String toString() {
if (cachedToString == null) {
String type;
if (getTag() == ConstantPoolEntry.CP_Fieldref) {
type = "FieldRef"; //$NON-NLS-1$
} else if (getTag() == ConstantPoolEntry.CP_Methodref) {
type = "MethoddRef"; //$NON-NLS-1$
} else if (getTag() == ConstantPoolEntry.CP_InterfaceMethodref) {
type = "InterfaceMethodRef"; //$NON-NLS-1$
} else {
type = "unknown"; //$NON-NLS-1$
}
cachedToString = type + ": " + className + "#" + nameAndType; //$NON-NLS-1$ //$NON-NLS-2$
}
return cachedToString;
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(classNameIndex);
dos.writeShort(nameAndTypeIndex);
}
}

@ -0,0 +1,73 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* String constant pool entry.
*/
public class CPString extends CPConstant {
private transient int nameIndex;
private final CPUTF8 name;
public CPString(CPUTF8 value, int globalIndex) {
super(ConstantPoolEntry.CP_String, value, globalIndex);
this.name = value;
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeShort(nameIndex);
}
public String toString() {
return "String: " + getValue();
}
/**
* Allows the constant pool entries to resolve their nested entries
*
* @param pool
*/
protected void resolve(ClassConstantPool pool) {
super.resolve(pool);
nameIndex = pool.indexOf(name);
}
protected ClassFileEntry[] getNestedClassFileEntries() {
return new ClassFileEntry[] { name };
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
int result = 1;
result = PRIME * result + name.hashCode();
cachedHashCode = result;
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
}

@ -0,0 +1,90 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* UTF8 constant pool entry, used for storing long Strings.
*/
public class CPUTF8 extends ConstantPoolEntry {
private final String utf8;
/**
* Creates a new CPUTF8 instance
*
* @param utf8
* @param globalIndex - index in CpBands
* @throws NullPointerException
* if utf8 is null
*/
public CPUTF8(String utf8, int globalIndex) {
super(ConstantPoolEntry.CP_UTF8, globalIndex);
this.utf8 = utf8;
if (utf8 == null) {
throw new NullPointerException("Null arguments are not allowed");
}
}
public CPUTF8(String string) {
this(string, -1);
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (this.getClass() != obj.getClass())
return false;
final CPUTF8 other = (CPUTF8) obj;
return utf8.equals(other.utf8);
}
private boolean hashcodeComputed;
private int cachedHashCode;
private void generateHashCode() {
hashcodeComputed = true;
final int PRIME = 31;
cachedHashCode = PRIME + utf8.hashCode();
}
public int hashCode() {
if (!hashcodeComputed)
generateHashCode();
return cachedHashCode;
}
public String toString() {
return "UTF8: " + utf8;
}
protected void writeBody(DataOutputStream dos) throws IOException {
dos.writeUTF(utf8);
}
public String underlyingString() {
return utf8;
}
public void setGlobalIndex(int index) {
globalIndex = index;
}
}

@ -0,0 +1,277 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.apache.harmony.unpack200.Segment;
/**
* The Class constant pool
*/
public class ClassConstantPool {
protected HashSet entriesContainsSet = new HashSet();
protected HashSet othersContainsSet = new HashSet();
private final HashSet mustStartClassPool = new HashSet();
protected Map indexCache;
public String toString() {
return entries.toString();
}
private final List others = new ArrayList(500);
private final List entries = new ArrayList(500);
private boolean resolved;
public ClassFileEntry add(ClassFileEntry entry) {
if (entry instanceof ByteCode) {
return null;
}
if (entry instanceof ConstantPoolEntry) {
if (entriesContainsSet.add(entry)) {
entries.add(entry);
}
} else {
if (othersContainsSet.add(entry)) {
others.add(entry);
}
}
return entry;
}
public void addNestedEntries() {
boolean added = true;
// initial assignment
ArrayList parents = new ArrayList(512);
ArrayList children = new ArrayList(512);
// adding old entries
parents.addAll(entries);
parents.addAll(others);
// while there any parents to traverse and at least one change in target
// storage was made
while(added || parents.size() > 0) {
children.clear();
int entriesOriginalSize = entries.size();
int othersOriginalSize = others.size();
// get the parents' children and add them to buffer
// concurrently add parents to target storage
for(int indexParents = 0; indexParents < parents.size(); indexParents++) {
ClassFileEntry entry = (ClassFileEntry) parents.get(indexParents);
// traverse children
ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
for(int c = 0; c < entryChildren.length; c++) {
children.add(entryChildren[c]);
}
boolean isAtStart = (entry instanceof ByteCode)
&& ((ByteCode) entry).nestedMustStartClassPool();
if (isAtStart) {
for (int c = 0; c < entryChildren.length; c++) {
mustStartClassPool.add(entryChildren[c]);
}
}
// add parent
add(entry);
}
added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize);
// parents are not needed anymore
// children now become parents
parents.clear();
parents.addAll(children);
}
}
public int indexOf(ClassFileEntry entry) {
if (!resolved)
throw new IllegalStateException(
"Constant pool is not yet resolved; this does not make any sense");
if (null == indexCache) {
throw new IllegalStateException("Index cache is not initialized!");
}
Integer entryIndex = ((Integer) indexCache.get(entry));
// If the entry isn't found, answer -1. Otherwise answer the entry.
if (entryIndex != null) {
return entryIndex.intValue() + 1;
}
return -1;
}
public int size() {
return entries.size();
}
public ClassFileEntry get(int i) {
if (!resolved)
throw new IllegalStateException(
"Constant pool is not yet resolved; this does not make any sense");
return (ClassFileEntry) entries.get(--i);
}
public void resolve(Segment segment) {
initialSort();
sortClassPool();
resolved = true;
for(int it = 0; it < entries.size(); it++) {
ClassFileEntry entry = (ClassFileEntry) entries.get(it);
entry.resolve(this);
}
for(int it = 0; it < others.size(); it++) {
ClassFileEntry entry = (ClassFileEntry) others.get(it);
entry.resolve(this);
}
}
private void initialSort() {
TreeSet inCpAll = new TreeSet(new Comparator() {
public int compare(Object arg0, Object arg1) {
return ((ConstantPoolEntry)arg0).getGlobalIndex() - ((ConstantPoolEntry)arg1).getGlobalIndex();
}
});
TreeSet cpUtf8sNotInCpAll = new TreeSet(new Comparator() {
public int compare(Object arg0, Object arg1) {
return ((CPUTF8)arg0).underlyingString().compareTo(((CPUTF8)arg1).underlyingString());
}
});
TreeSet cpClassesNotInCpAll = new TreeSet(new Comparator() {
public int compare(Object arg0, Object arg1) {
return ((CPClass)arg0).getName().compareTo(((CPClass)arg1).getName());
}
});
for(int index = 0; index < entries.size(); index++) {
ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(index);
if(entry.getGlobalIndex() == -1) {
if (entry instanceof CPUTF8) {
cpUtf8sNotInCpAll.add(entry);
} else if (entry instanceof CPClass) {
cpClassesNotInCpAll.add(entry);
} else {
throw new Error("error");
}
} else {
inCpAll.add(entry);
}
}
entries.clear();
entries.addAll(inCpAll);
entries.addAll(cpUtf8sNotInCpAll);
entries.addAll(cpClassesNotInCpAll);
}
public List entries() {
return Collections.unmodifiableList(entries);
}
protected void sortClassPool() {
// Now that everything has been resolved, do one
// final sort of the class pool. This fixes up
// references to objects which need to be at the
// start of the class pool
ArrayList startOfPool = new ArrayList(entries.size());
ArrayList finalSort = new ArrayList(entries.size());
for(int i = 0; i < entries.size(); i++) {
ClassFileEntry nextEntry = (ClassFileEntry) entries.get(i);
if (mustStartClassPool.contains(nextEntry)) {
startOfPool.add(nextEntry);
} else {
finalSort.add(nextEntry);
}
}
// copy over and rebuild the cache
//
indexCache = new HashMap(entries.size());
int index = 0;
entries.clear();
for(int itIndex = 0; itIndex < startOfPool.size(); itIndex++) {
ClassFileEntry entry = (ClassFileEntry) startOfPool.get(itIndex);
indexCache.put(entry, new Integer(index));
if (entry instanceof CPLong || entry instanceof CPDouble) {
entries.add(entry); // these get 2 slots because of their size
entries.add(entry);
index += 2;
} else {
entries.add(entry);
index += 1;
}
}
for(int itFinal = 0; itFinal < finalSort.size(); itFinal++) {
ClassFileEntry entry = (ClassFileEntry) finalSort.get(itFinal);
indexCache.put(entry, new Integer(index));
if (entry instanceof CPLong || entry instanceof CPDouble) {
entries.add(entry); // these get 2 slots because of their size
entries.add(entry);
index += 2;
} else {
entries.add(entry);
index += 1;
}
}
}
public ClassFileEntry addWithNestedEntries(ClassFileEntry entry) {
add(entry);
ClassFileEntry[] nestedEntries = entry.getNestedClassFileEntries();
for (int i = 0; i < nestedEntries.length; i++) {
addWithNestedEntries(nestedEntries[i]);
}
return entry;
}
}

@ -0,0 +1,73 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* ClassFile is used to represent and write out Java class files.
*/
public class ClassFile {
public int major;
public int minor;
private final int magic = 0xCAFEBABE;
public ClassConstantPool pool = new ClassConstantPool();
public int accessFlags;
public int thisClass;
public int superClass;
public int[] interfaces;
public ClassFileEntry[] fields;
public ClassFileEntry[] methods;
public Attribute[] attributes;
public void write(DataOutputStream dos) throws IOException {
dos.writeInt(magic);
dos.writeShort(minor);
dos.writeShort(major);
dos.writeShort(pool.size() + 1);
for (int i = 1; i <= pool.size(); i++) {
ConstantPoolEntry entry;
(entry = (ConstantPoolEntry) pool.get(i)).doWrite(dos);
// Doubles and longs take up two spaces in the pool, but only one
// gets written
if (entry.getTag() == ConstantPoolEntry.CP_Double
|| entry.getTag() == ConstantPoolEntry.CP_Long)
i++;
}
dos.writeShort(accessFlags);
dos.writeShort(thisClass);
dos.writeShort(superClass);
dos.writeShort(interfaces.length);
for (int i = 0; i < interfaces.length; i++) {
dos.writeShort(interfaces[i]);
}
dos.writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
fields[i].write(dos);
}
dos.writeShort(methods.length);
for (int i = 0; i < methods.length; i++) {
methods[i].write(dos);
}
dos.writeShort(attributes.length);
for (int i = 0; i < attributes.length; i++) {
attributes[i].write(dos);
}
}
}

@ -0,0 +1,65 @@
/*
* 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.
*/
package org.apache.harmony.unpack200.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* The abstract superclass for all types of class file entries.
*/
public abstract class ClassFileEntry {
protected static final ClassFileEntry[] NONE = new ClassFileEntry[0];
private boolean resolved;
protected abstract void doWrite(DataOutputStream dos) throws IOException;
public abstract boolean equals(Object arg0);
protected ClassFileEntry[] getNestedClassFileEntries() {
return NONE;
}
public abstract int hashCode();
/**
* Allows the constant pool entries to resolve their nested entries
*
* @param pool
*/
protected void resolve(ClassConstantPool pool) {
resolved = true;
}
protected boolean isResolved() {
return resolved;
}
protected int objectHashCode() {
return super.hashCode();
}
public abstract String toString();
public final void write(DataOutputStream dos) throws IOException {
if (!resolved)
throw new IllegalStateException("Entry has not been resolved");
doWrite(dos);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save