/*
*
* 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 macromedia.abc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import macromedia.asc.embedding.avmplus.ActionBlockConstants;
import macromedia.asc.parser.MetaDataEvaluator;
import macromedia.asc.parser.MetaDataNode;
import macromedia.asc.semantics.MetaData;
import macromedia.asc.semantics.Value;
import macromedia.asc.util.Decimal128;
@SuppressWarnings("nls")
public class AbcData implements java.io.Externalizable
{
/**
* Attempt to read the cache from the persistent store when set.
*/
static boolean readCache = true;
/**
* Attempt to write the cache to persistent store when set.
*/
static boolean saveCache = true;
/**
* Object used to serialize access to the cache.
*/
static final Object serializationLock = new Object();
public static boolean contains(String src_name)
{
return getCache().cache.containsKey(src_name);
}
public static AbcData getCacheEntry(String src_name)
{
return getCache().get(src_name);
}
public void finish()
{
if ( !( dirty && saveCache) )
{
getCache().remove(this);
}
}
private boolean dirty = false;
private String scriptName;
private static AbcDataCache instance;
/**
* @return the singleton instance of the cache.
*/
private static AbcDataCache getCache()
{
synchronized(serializationLock)
{
if ( null == instance )
deserializeCache (new File("/tmp/AbcDataCache.ser"));
return instance;
}
}
/**
* Deserialize the AbcDataCache if feasible.
* @param input - the File that holds the serialized cache.
* @post The instance field holds a deserialized or new cache.
* @throws nothing -- errors dealt with here.
*/
private static void deserializeCache(File input)
{
{
if ( null == instance && readCache && input.canRead() )
{
try
{
ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(input));
instance = (AbcDataCache)objIn.readObject();
objIn.close();
instance.file = input;
}
catch ( java.io.InvalidClassException incompatible_serialized_class )
{
// Previously serialized class can't be deserialized,
// start a new cache.
}
catch ( Exception ex)
{
// TODO: Better reporting.
ex.printStackTrace();
}
}
if ( null == instance )
instance = new AbcDataCache(input);
}
}
public static void preload()
{
new Thread() {
@Override
public void run() {
getCache();
}
}.start();
}
private static class AbcDataCache implements java.io.Externalizable
{
private Map<String, AbcData> cache;
private boolean dirty;
private File file;
AbcDataCache(File f)
{
cache = new HashMap<String,AbcData>();
file = f;
dirty = false;
if ( saveCache )
{
Runtime.getRuntime().addShutdownHook(
new Thread() {
@Override
public void run()
{
// TODO: This shutdown hook should be a last resort!
AbcData.instance.flush();
}
}
);
}
}
public void flush()
{
if ( this.dirty )
{
try
{
ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(this.file));
objOut.writeObject(this);
objOut.close();
this.dirty = false;
System.out.println("Serialized AbcDataCache to " + this.file.getCanonicalPath()); //$NON-NLS-1$
}
catch ( Exception ex)
{
// Better logging would be nice, but a shutdown
// hook can't depend on any facilities but the
// most basic.
ex.printStackTrace();
}
}
}
/**
* Retrieve or create cached AbcData for a script.
* @param script_name - the script name.
* @return the script's AbcData entry.
* @post this.dirty set if the AbcData was created.
*/
AbcData get(String script_name)
{
if ( !cache.containsKey(script_name) )
{
setDirty();
cache.put(script_name, new AbcData(script_name));
cache.get(script_name).dirty = true;
}
return cache.get(script_name);
}
/**
* Set the cache's dirty bit.
* @param is_dirty
* @post static maps cleared.
*/
private void setDirty()
{
this.dirty = true;
}
void remove(AbcData data)
{
if ( !(dirty && saveCache) )
{
cache.remove(data.scriptName);
}
}
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
cache = (Map<String,AbcData>)in.readObject();
dirty = false;
file = new File(in.readObject().toString());
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeObject(cache);
out.writeObject(file.getCanonicalPath());
}
}
/*
* Housekeeping functions
*/
public AbcData(String script_name)
{
this.scriptName = script_name;
// see deserialization code,
// which resets the dirty bit.
this.dirty = true;
}
public boolean isBuilding()
{
return this.dirty;
}
/*
* Traits
*/
public class Trait implements java.io.Externalizable
{
int nameIndex;
int kind;
int [] data;
int[] metadata;
public Trait(int name_index, int kind, int[] data)
{
this.nameIndex = name_index;
this.kind = kind;
this.data = data;
}
public void addMetadata(int[] metadata)
{
this.metadata = metadata;
}
public int getNameIndex() {
return this.nameIndex;
}
public int getAttrs()
{
return kind >> 4;
}
public int getTag()
{
return kind & 0xf;
}
public boolean hasMetadata() {
return ( getAttrs() & ActionBlockConstants.TRAIT_FLAG_metadata) != 0;
}
public boolean isConstTrait() {
return (getTag() == ActionBlockConstants.TRAIT_Const);
}
public int[] getMetadata()
{
return this.metadata;
}
public int getSlotId() {
assert(getTag() == ActionBlockConstants.TRAIT_Var || getTag() == ActionBlockConstants.TRAIT_Const || getTag() == ActionBlockConstants.TRAIT_Class);
return data[0];
}
public int getTypeName() {
assert(getTag() == ActionBlockConstants.TRAIT_Var || getTag() == ActionBlockConstants.TRAIT_Const);
return data[1];
}
public int getValueIndex() {
assert(getTag() == ActionBlockConstants.TRAIT_Var || getTag() == ActionBlockConstants.TRAIT_Const);
return data[2];
}
public int getValueKind() {
assert(getTag() == ActionBlockConstants.TRAIT_Var || getTag() == ActionBlockConstants.TRAIT_Const);
return data[3];
}
public int getDispId() {
assert(getTag() == ActionBlockConstants.TRAIT_Method || getTag() == ActionBlockConstants.TRAIT_Getter || getTag() == ActionBlockConstants.TRAIT_Setter);
return data[0];
}
public int getMethodId() {
assert(getTag() == ActionBlockConstants.TRAIT_Method || getTag() == ActionBlockConstants.TRAIT_Getter || getTag() == ActionBlockConstants.TRAIT_Setter);
return data[1];
}
public int getClassId() {
assert(getTag() == ActionBlockConstants.TRAIT_Class);
return data[1];
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
nameIndex = in.readInt();
kind = in.readByte();
data = (int[]) in.readObject();
metadata = (int[]) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(nameIndex);
out.writeByte(kind);
out.writeObject(data);
out.writeObject(metadata);
}
}
/*
* NameData is an unstructured view of deserialized name data.
*/
public class NameData implements java.io.Externalizable
{
int kind = 0;
int[] params;
NameData(int kind, int[] params)
{
this.kind = kind;
this.params = params;
}
public int getKind()
{
return this.kind;
}
public int[] getParams()
{
return this.params;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
kind = in.readByte();
params = (int[])in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeByte(kind);
out.writeObject(params);
}
Set<Integer> getVersions()
{
Namespace ns = null;
// If the name's a multiname, get the first namespace
// since they all share the same base URI.
if ( ActionBlockConstants.CONSTANT_Multiname == kind || ActionBlockConstants.CONSTANT_MultinameA == kind )
{
int[] nss = AbcData.this.namespaceSets[this.params[1]].namespaceIds;
ns = (nss.length > 0)? namespaces[nss[0]]: null;
}
else
ns = namespaces[params[0]];
switch ( kind )
{
case ActionBlockConstants.CONSTANT_Qname:
case ActionBlockConstants.CONSTANT_QnameA:
ns = namespaces[params[0]];
break;
case ActionBlockConstants.CONSTANT_Multiname:
case ActionBlockConstants.CONSTANT_MultinameA:
int[] nss = AbcData.this.namespaceSets[this.params[1]].namespaceIds;
ns = (nss.length > 0)? namespaces[nss[0]]: null;
break;
default:
ns = null;
}
if ( ns != null )
{
String uri = strings[ns.nameOffset];
return getVersion(uri);
}
return null;
}
Set<Integer> getVersion(String uri)
{
if (uri.length() == 0)
return null;
int last = uri.codePointAt(uri.length()-1);
if(last >= 0xE000 && last <= 0xF000)
{
Set<Integer> result = new TreeSet<Integer>();
result.add(last-0xE000);
return result;
}
return null;
}
}
/**
* Names defined in this script.
*/
private NameData[] nameData;
/**
* Temporary holder for names read from abc files.
* Types are:
* QName: namespace id,name id
* Typename: QName reference (base), type parameter list of QName references
* Multiname: name, namespaceset id
*/
public class BinaryMN
{
public int kind = 0; // MultiName type
public int nameID = 0; // name CPool string index (not used for CONSTANT_Typename)
public int nsID = 0; // namespace or namespaceset id
public boolean nsIsSet = false;
public int baseMN; // CONSTANT_Typename: base name CPool MN index (e.g. 'Vector'
public int[] params; // CONSTANT_TypeName: parameter type CPool MN index (e.g. Vector<'int'>)
public Set<Integer> versions; // optional: supports version gather
/**
* Construct a non-parameterized name.
* @param name_index
* @param name_space
* @param ns_is_set
* @param versions
*/
BinaryMN(int kind, int name_index, int name_space, boolean ns_is_set, Set<Integer>versions)
{
this.kind = kind;
this.nameID = name_index;
this.nsID = name_space;
this.nsIsSet = ns_is_set;
this.versions = versions;
}
/**
* Construct a parameterized name.
*/
BinaryMN(int kind, int base_name, int[] plist)
{
this.kind = kind;
this.baseMN = base_name;
this.params = plist;
}
}
private transient BinaryMN[] binaryMultinames;
public BinaryMN getName(int idx)
{
if ( null == this.binaryMultinames[idx] )
{
NameData nd = this.nameData[idx];
int name_index = 0;
int name_space = 0;
boolean ns_is_set = false;
switch ( nd.kind )
{
case ActionBlockConstants.CONSTANT_Qname:
case ActionBlockConstants.CONSTANT_QnameA:
name_space = nd.params[0];
name_index = nd.params[1];
ns_is_set = false;
this.binaryMultinames[idx] = new BinaryMN(nd.kind, name_index, name_space, ns_is_set, nd.getVersions());
break;
case ActionBlockConstants.CONSTANT_TypeName:
name_index = nd.params[0];
int count = nd.params.length - 2;
assert( count == nd.params[1]);
int[] plist = new int[count];
for( int i = 0; i < count; ++i )
plist[i] = nd.params[i+2];
this.binaryMultinames[idx] = new BinaryMN(nd.kind, name_index, plist);
break;
case ActionBlockConstants.CONSTANT_Multiname:
case ActionBlockConstants.CONSTANT_MultinameA:
name_index = nd.params[0];
name_space = nd.params[1];
ns_is_set = true;
this.binaryMultinames[idx] = new BinaryMN(nd.kind, name_index, name_space, ns_is_set, nd.getVersions());
break;
default:
throw new IllegalArgumentException("Unexpected multiname type: " + nd.kind); //$NON-NLS-1$
}
}
return this.binaryMultinames[idx];
}
AbcParser parser;
public void setParser(AbcParser parser)
{
this.parser = parser;
}
/*
* Namespace is an ABC-centric view of a namespace.
*/
public class Namespace implements java.io.Externalizable
{
int nameOffset;
public byte nsKind;
Namespace(int name_offset, int ns_kind)
{
this.nameOffset = name_offset;
this.nsKind = (byte)ns_kind;
}
public String getName()
{
assert(nameOffset < strings.length);
return strings[nameOffset];
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
nameOffset = in.readInt();
nsKind = in.readByte();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(nameOffset);
out.writeByte(nsKind);
}
}
Namespace[] namespaces;
public Namespace getNamespace(int i)
{
return this.namespaces[i];
}
/*
* Namespace sets
*/
public class NamespaceSet implements java.io.Externalizable
{
int[] namespaceIds;
NamespaceSet(int[] nss_ids)
{
this.namespaceIds = nss_ids;
}
public int[] getNamespaceIds()
{
return this.namespaceIds;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
namespaceIds = (int[])in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeObject(namespaceIds);
}
}
NamespaceSet[] namespaceSets;
public void addNamespaceSet(int nss_id, int[] namespaces)
{
if ( !isBuilding() )
return;
assert(this.namespaceSets != null && nss_id < this.namespaceSets.length && null == this.namespaceSets[nss_id]);
NamespaceSet nss = new NamespaceSet(namespaces);
this.namespaceSets[nss_id] = nss;
}
public NamespaceSet getNamespaceSet(int id)
{
assert(id < this.namespaceSets.length);
return this.namespaceSets[id];
}
/*
* Strings
*/
String[] strings;
public String getString(int i)
{
return this.strings[i];
}
/*
* Ints
*/
int[] ints;
public int getInt(int i)
{
return this.ints[i];
}
/*
* Uints
*/
long[] uints;
public long getUint(int i)
{
return uints[i];
}
/*
* Doubles
*/
double[] doubles;
public double getDouble(int i)
{
return doubles[i];
}
/*
* Decimals
*/
private Decimal128[] decimals;
public Decimal128 getDecimal(int i)
{
return decimals[i];
}
/*
* Methods
*/
public class Method implements java.io.Externalizable
{
private int returnType;
int[] paramTypes;
private int nameIndex;
int flags;
int[] optionalParamTypes;
int[] optionalParamKinds;
int[] paramNames;
public Method(int return_type, int[] param_types,
int name_index, int flags, int[] optional_param_types,
int[] optional_param_kinds, int[] param_names)
{
this.returnType = return_type;
this.paramTypes = param_types;
this.nameIndex = name_index;
this.flags = flags;
this.optionalParamTypes = optional_param_types;
this.optionalParamKinds = optional_param_kinds;
this.paramNames = param_names;
}
public int getReturnType() {
return returnType;
}
public int[] getParamTypes() {
return this.paramTypes;
}
public int getNameIndex() {
return nameIndex;
}
public int getFlags() {
return this.flags;
}
public int[] getOptionalParamTypes() {
return optionalParamTypes;
}
public int[] getOptionalParamKinds() {
return optionalParamKinds;
}
public String[] getParamNames() {
String[] result = new String[paramNames.length];
for ( int i = 0; i < paramNames.length; i++ )
result[i] = strings[paramNames[i]];
return result;
}
public boolean getNeedsRest() {
return (flags & ActionBlockConstants.METHOD_Needrest) != 0 || (flags & ActionBlockConstants.METHOD_IgnoreRest) != 0;
}
public boolean getHasOptional() {
return (flags & ActionBlockConstants.METHOD_HasOptional) != 0;
}
public boolean getHasParamNames() {
return (flags & ActionBlockConstants.METHOD_HasParamNames) != 0;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
returnType = in.readInt();
paramTypes = (int[])in.readObject();
nameIndex = in.readInt();
flags = in.readInt();
optionalParamTypes = (int[])in.readObject();
optionalParamKinds = (int[])in.readObject();
paramNames = (int[])in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(returnType);
out.writeObject(paramTypes);
out.writeInt(nameIndex);
out.writeInt(flags);
out.writeObject(optionalParamTypes);
out.writeObject(optionalParamKinds);
out.writeObject(paramNames);
}
}
private Method[] methods;
public Method getMethod(int index)
{
assert(index >= 0 && index < this.methods.length);
return this.methods[index];
}
/*
* Metadata
*/
public class Metadata implements java.io.Externalizable
{
int nameIndex;
int[] keys;
int[] values;
Metadata(int name_index, int[] keys, int[] values)
{
this.nameIndex = name_index;
this.keys = keys;
this.values = values;
assert(keys.length == values.length);
}
public int getDataCount() {
return keys.length;
}
public String getName()
{
return getString(this.nameIndex);
}
public String getKey(int idx)
{
assert(idx >= 0 && idx < keys.length);
return getString(keys[idx]);
}
public String getValue(int idx)
{
assert(idx >= 0 && idx < values.length);
return getString(values[idx]);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
nameIndex = in.readInt();
keys = (int[]) in.readObject();
values = (int[]) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(nameIndex);
out.writeObject(keys);
out.writeObject(values);
}
}
private Metadata[] raw_metadata;
private transient MetaData[] semantic_metadata;
public MetaData getMetadata(int idx, MetaDataNode metaNode )
{
if ( null == this.semantic_metadata[idx] )
{
Metadata m = this.raw_metadata[idx];
metaNode.setId(m.getName());
int[] keys = m.keys;
int[] raw_values = m.values;
Value[] vals = new Value[keys.length];
for(int i = 0; i < keys.length; ++i )
{
String value = strings[raw_values[i]];
vals[i] = ( keys[i] == 0 )
? new MetaDataEvaluator.KeylessValue(value)
: new MetaDataEvaluator.KeyValuePair( strings[keys[i]], value);
}
metaNode.setValues(vals);
semantic_metadata[idx] = metaNode.getMetadata();
}
return this.semantic_metadata[idx];
}
/*
* InstanceInfo
*/
public class InstanceInfo implements java.io.Externalizable
{
int nameIndex;
int superIndex;
int flags;
int protectedNs;
int[] interfaces;
int initIndex;
Trait[] iTraits;
InstanceInfo(int nameIndex, int superIndex, int flags, int protected_ns, int[] interfaces, int init_index, Trait[] itraits)
{
this.nameIndex = nameIndex;
this.superIndex = superIndex;
this.flags = flags;
this.protectedNs = protected_ns;
this.interfaces = interfaces;
this.initIndex = init_index;
this.iTraits = itraits;
}
public int getInstanceNameID() {
return nameIndex;
}
public int getSuperID() {
return superIndex;
}
public int getFlags() {
return flags;
}
public int getProtectedNs() {
return protectedNs;
}
public int[] getInterfaces() {
return interfaces;
}
public int getInitIndex() {
return initIndex;
}
public Trait[] getITraits() {
return iTraits;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
nameIndex = in.readInt();
superIndex = in.readInt();
flags = in.readInt();
protectedNs = in.readInt();
interfaces = (int[]) in.readObject();
initIndex = in.readInt();
iTraits = (Trait[])in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(nameIndex);
out.writeInt(superIndex);
out.writeInt(flags);
out.writeInt(protectedNs);
out.writeObject(interfaces);
out.writeInt(initIndex);
out.writeObject(iTraits);
}
}
private InstanceInfo[] instanceInfos;
public InstanceInfo getInstanceInfo(int i)
{
return this.instanceInfos[i];
}
/*
* ClassInfo
*/
public class ClassInfo implements java.io.Externalizable
{
int clinitIndex;
Trait[] cTraits;
ClassInfo(int clinit_index, Trait[] ctraits)
{
this.clinitIndex = clinit_index;
this.cTraits = ctraits;
}
public int getClinitIndex() {
return clinitIndex;
}
public Trait[] getCTraits() {
return cTraits;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
clinitIndex = in.readInt();
cTraits = (Trait[]) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(clinitIndex);
out.writeObject(cTraits);
}
}
private ClassInfo[] classInfos;
public int getClassInfoSize() {
return this.classInfos.length;
}
public ClassInfo getClassInfo(int idx)
{
assert(this.classInfos != null && idx >= 0 && idx <= this.classInfos.length);
return this.classInfos[idx];
}
/*
* ScriptInfo
*/
public class ScriptInfo implements java.io.Externalizable
{
int initIndex;
Trait[] scriptTraits;
ScriptInfo(int init_index, Trait[] script_traits)
{
this.initIndex = init_index;
this.scriptTraits = script_traits;
}
public int getInitIndex() {
return this.initIndex;
}
public Trait[] getScriptTraits() {
return this.scriptTraits;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
initIndex = in.readInt();
scriptTraits = (Trait[]) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(initIndex);
out.writeObject(scriptTraits);
}
}
ScriptInfo[] scriptInfos;
public int getScriptInfoSize()
{
return this.scriptInfos.length;
}
public ScriptInfo getScriptInfo(int idx)
{
assert(this.scriptInfos != null && idx >= 0 && idx < this.scriptInfos.length);
return this.scriptInfos[idx];
}
/**
* Serialize the AbcData to an ObjectOutput stream.
*/
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeObject(classInfos);
out.writeObject(doubles);
out.writeObject(instanceInfos);
out.writeObject(ints);
out.writeObject(raw_metadata);
out.writeObject(methods);
out.writeObject(namespaces);
out.writeObject(namespaceSets);
out.writeObject(nameData);
out.writeObject(scriptInfos);
out.writeObject(scriptName);
out.writeObject(strings);
out.writeObject(uints);
}
/**
* Deserialize the AbcData.
*/
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
classInfos = (ClassInfo[])in.readObject();
doubles = (double[]) in.readObject();
instanceInfos = (InstanceInfo[])in.readObject();
ints = (int[])in.readObject();
raw_metadata = (Metadata[])in.readObject();
semantic_metadata = new MetaData[raw_metadata.length];
methods = (Method[]) in.readObject();
namespaces = (Namespace[]) in.readObject();
namespaceSets = (NamespaceSet[])in.readObject();
nameData = (NameData[])in.readObject();
scriptInfos = (ScriptInfo[])in.readObject();
scriptName = (String)in.readObject();
strings = (String[])in.readObject();
uints = (long[]) in.readObject();
dirty = false;
}
public void readAbc(byte[] raw_abc)
{
if (!this.isBuilding() )
return;
readAbc(new BytecodeBuffer(raw_abc));
}
public void readAbc(BytecodeBuffer buf)
{
if (!this.isBuilding() )
return;
int minor_version = buf.readU16();
/*int major_version =*/ buf.readU16();
// Scan the bytecode for the stuff we care about
scanCpool(buf, minor_version >= ActionBlockConstants.MINORwithDECIMAL);
scanMethods(buf);
scanMetadata(buf);
scanClasses(buf);
scanScripts(buf);
}
void scanCpool(BytecodeBuffer buf, boolean hasDecimal)
{
int size;
size = buf.readU32();
this.ints = new int[size];
for (int i = 1; i < size; i++)
{
this.ints[i] = buf.readU32();
}
size = buf.readU32();
this.uints = new long[size];
for (int i = 1; i < size; i++)
{
this.uints[i] = buf.readU32() & 0xFFFFFFFFL;
}
size = buf.readU32();
this.doubles = new double[size];
for (int i = 1; i < size; i++)
{
this.doubles[i] = buf.readDouble();
}
if (hasDecimal)
{
size = buf.readU32();
this.decimals = new Decimal128[size];
for (int i = 1; i < size; i++)
{
byte[] rep = buf.readBytes(16);
decimals[i] = new Decimal128(rep);
}
}
size = buf.readU32();
this.strings = new String[size];
this.strings[0] = "".intern();
for (int i = 1; i < size; i++)
{
int length = buf.readU32();
this.strings[i] = buf.readString(length).intern();
buf.skip(length); // readString() doesn't reset pos pointer.
}
size = buf.readU32();
this.namespaces = new Namespace[size];
for (int i = 1; i < size; i++)
{
int ns_kind = buf.readU8(); // kind byte
int name_offset = buf.readU32();
this.namespaces[i] = new Namespace(name_offset, ns_kind);
}
size = buf.readU32();
this.namespaceSets = new NamespaceSet[size];
for (int i = 1; i < size; i++)
{
int count = buf.readU32(); // count
int[] ns_ids = new int[count];
for(int q =0; q < count; ++q)
{
ns_ids[q] = buf.readU32();
}
this.namespaceSets[i] = new NamespaceSet(ns_ids);
}
size = buf.readU32();
this.nameData = new NameData[size];
this.binaryMultinames = new BinaryMN[size];
for (int i = 1; i < size; i++)
{
int kind = buf.readU8();
switch (kind)
{
case ActionBlockConstants.CONSTANT_Qname:
case ActionBlockConstants.CONSTANT_QnameA:
this.nameData[i] = new NameData(kind, new int[] {buf.readU32(), buf.readU32()});
break;
case ActionBlockConstants.CONSTANT_RTQname:
case ActionBlockConstants.CONSTANT_RTQnameA:
this.nameData[i] = new NameData(kind, new int[] {buf.readU32()});
break;
case ActionBlockConstants.CONSTANT_Multiname:
case ActionBlockConstants.CONSTANT_MultinameA:
this.nameData[i] = new NameData(kind, new int[] {buf.readU32(), buf.readU32()} );
break;
case ActionBlockConstants.CONSTANT_MultinameL:
case ActionBlockConstants.CONSTANT_MultinameLA:
this.nameData[i] = new NameData(kind, new int[] {buf.readU32()});
break;
case ActionBlockConstants.CONSTANT_TypeName:
int name_index = buf.readU32(); // name index
int count = buf.readU32(); // param count;
int[] entries = new int[count+2];
entries[0] = name_index;
entries[1] = count;
for ( int k = 0; k < count; k++)
entries[k+2] = buf.readU32();
this.nameData[i] = new NameData(kind, entries);
break;
case ActionBlockConstants.CONSTANT_RTQnameL:
case ActionBlockConstants.CONSTANT_RTQnameLA:
this.nameData[i] = new NameData(kind, null);
break;
default:
throw new RuntimeException("bad multiname type: " + kind);
}
}
}
void scanMethods(BytecodeBuffer buf)
{
int methodEntries = buf.readU32();
this.methods = new Method[methodEntries];
for (int i = 0; i < methodEntries; i++)
{
int param_count = buf.readU32();
int return_type = buf.readU32();
int[] param_types = new int[param_count];
for ( int j = 0; j < param_count; j++ )
param_types[j] = buf.readU32();
int name_index = buf.readU32();
int flags = buf.readU8();
int optional_param_count = ((flags & ActionBlockConstants.METHOD_HasOptional) != 0)? buf.readU32(): 0;
int[] optional_param_types = new int[optional_param_count];
int[] optional_param_kinds = new int[optional_param_count];
for( int q = 0; q < optional_param_count; ++q )
{
optional_param_types[q] = buf.readU32();
optional_param_kinds[q] = buf.readU8();
}
int param_name_count = ((flags & ActionBlockConstants.METHOD_HasParamNames)!=0) ? param_count : 0;
int[] param_names = new int[param_name_count];
for( int q = 0; q < param_name_count; ++q )
{
param_names[q] = buf.readU32();
}
this.methods[i] = new Method(return_type, param_types, name_index, flags, optional_param_types, optional_param_kinds, param_names);
}
}
void scanMetadata(BytecodeBuffer buf)
{
int metadataEntries = buf.readU32();
this.raw_metadata = new Metadata[metadataEntries];
this.semantic_metadata = new MetaData[metadataEntries];
for (int i = 0; i < metadataEntries; i++)
{
int name_index = buf.readU32();
int value_count = buf.readU32(); //returnType
int[] keys = new int[value_count];
int[] values = new int[value_count];
for ( int j = 0; j < value_count; j++ )
{
keys[j] = buf.readU32();
}
for(int j = 0; j < value_count; j++ )
{
values[j] = buf.readU32();
}
this.raw_metadata[i] = new Metadata( name_index, keys, values );
}
}
void scanClasses(BytecodeBuffer buf)
{
int classEntries = buf.readU32();
this.classInfos = new ClassInfo[classEntries];
this.instanceInfos = new InstanceInfo[classEntries];
// InstanceInfos
for(int i = 0; i < classEntries; ++i)
{
int name_index = buf.readU32();
int super_index = buf.readU32(); //super_index
int flags = buf.readU8();
int protected_ns = 0;
if ((flags & ActionBlockConstants.CLASS_FLAG_protected) != 0)
{
protected_ns = buf.readU32();
}
int interface_count = buf.readU32();
int[] interfaces = new int[interface_count];
for ( int j = 0; j < interface_count; j++ )
interfaces[j] = buf.readU32();
int init_index = buf.readU32();
AbcData.Trait[] itraits = scanTraits(buf);
this.instanceInfos[i] = new InstanceInfo(name_index, super_index, flags, protected_ns, interfaces, init_index, itraits);
}
// ClassInfos
for(int i = 0; i < classEntries; ++i)
{
int clinit_index = buf.readU32();
AbcData.Trait[] ctraits = scanTraits(buf);
this.classInfos[i] = new ClassInfo(clinit_index, ctraits);
}
}
void scanScripts(BytecodeBuffer buf)
{
int script_count = buf.readU32();
this.scriptInfos = new ScriptInfo[script_count];
for(int i = 0 ; i < script_count; ++i )
{
this.scriptInfos[i] = new ScriptInfo(buf.readU32(), scanTraits(buf));
}
}
/**
* Read traits from the bytecode into AbcData cache form
* @return the deserialized traits.
*/
AbcData.Trait[] scanTraits(BytecodeBuffer buf)
{
int count = buf.readU32();
AbcData.Trait[] result = new AbcData.Trait[count];
for (int i = 0; i < count; i++)
{
int name_index = buf.readU32();
int kind = buf.readU8();
int tag = kind & 0x0f;
AbcData.Trait new_trait = null;
switch (tag)
{
case ActionBlockConstants.TRAIT_Var:
case ActionBlockConstants.TRAIT_Const:
{
int slot_id = buf.readU32();
int trait_type = buf.readU32();
int value_index = buf.readU32();
int value_kind = 0;
if( value_index > 0 )
value_kind = buf.readU8();
new_trait = this.new Trait(name_index, kind, new int[] { slot_id, trait_type, value_index, value_kind});
break;
}
case ActionBlockConstants.TRAIT_Method:
case ActionBlockConstants.TRAIT_Getter:
case ActionBlockConstants.TRAIT_Setter:
{
int disp_id = buf.readU32();
int method = buf.readU32();
new_trait = this.new Trait(name_index, kind, new int[] {disp_id, method} );
break;
}
case ActionBlockConstants.TRAIT_Class:
case ActionBlockConstants.TRAIT_Function:
{
int slot_id = buf.readU32();
int class_id = buf.readU32();
new_trait = this.new Trait(name_index, kind, new int[] { slot_id, class_id } );
break;
}
default:
break;
//throw new DecoderException("Invalid trait kind: " + kind);
}
assert(new_trait != null);
result[i] = new_trait;
if( new_trait.hasMetadata() )
{
int metadata_count = buf.readU32();
int[] metadata = new int[metadata_count];
for ( int j = 0; j < metadata_count; j++)
metadata[j] = buf.readU32();
new_trait.addMetadata(metadata);
}
}
return result;
}
}