git-svn-id: https://svn.apache.org/repos/asf/harmony/enhanced/trunk@926318 13f79535-47bb-0310-9956-ffa450edef68master
commit
57d03126b8
@ -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 < L; all prior values |
||||||
|
* must be > 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 |
||||||
|
* < 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…
Reference in new issue