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