blob: 93c9b1a84350d465d3e7cb8d296a2b09f9dbe251 [file] [log] [blame]
/*
*
* 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.flex.abc;
import static org.apache.flex.abc.ABCConstants.CONSTANT_Multiname;
import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameA;
import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameL;
import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameLA;
import static org.apache.flex.abc.ABCConstants.CONSTANT_Qname;
import static org.apache.flex.abc.ABCConstants.CONSTANT_QnameA;
import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQname;
import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameA;
import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameL;
import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameLA;
import static org.apache.flex.abc.ABCConstants.CONSTANT_TypeName;
import static org.apache.flex.abc.ABCConstants.OP_applytype;
import static org.apache.flex.abc.ABCConstants.OP_astype;
import static org.apache.flex.abc.ABCConstants.OP_bkptline;
import static org.apache.flex.abc.ABCConstants.OP_call;
import static org.apache.flex.abc.ABCConstants.OP_callproperty;
import static org.apache.flex.abc.ABCConstants.OP_callproplex;
import static org.apache.flex.abc.ABCConstants.OP_callpropvoid;
import static org.apache.flex.abc.ABCConstants.OP_callstatic;
import static org.apache.flex.abc.ABCConstants.OP_callsuper;
import static org.apache.flex.abc.ABCConstants.OP_callsupervoid;
import static org.apache.flex.abc.ABCConstants.OP_coerce;
import static org.apache.flex.abc.ABCConstants.OP_construct;
import static org.apache.flex.abc.ABCConstants.OP_constructprop;
import static org.apache.flex.abc.ABCConstants.OP_constructsuper;
import static org.apache.flex.abc.ABCConstants.OP_debug;
import static org.apache.flex.abc.ABCConstants.OP_debugfile;
import static org.apache.flex.abc.ABCConstants.OP_debugline;
import static org.apache.flex.abc.ABCConstants.OP_declocal;
import static org.apache.flex.abc.ABCConstants.OP_declocal_i;
import static org.apache.flex.abc.ABCConstants.OP_deleteproperty;
import static org.apache.flex.abc.ABCConstants.OP_dxns;
import static org.apache.flex.abc.ABCConstants.OP_finddef;
import static org.apache.flex.abc.ABCConstants.OP_findproperty;
import static org.apache.flex.abc.ABCConstants.OP_findpropstrict;
import static org.apache.flex.abc.ABCConstants.OP_getdescendants;
import static org.apache.flex.abc.ABCConstants.OP_getglobalslot;
import static org.apache.flex.abc.ABCConstants.OP_getlex;
import static org.apache.flex.abc.ABCConstants.OP_getlocal;
import static org.apache.flex.abc.ABCConstants.OP_getlocal0;
import static org.apache.flex.abc.ABCConstants.OP_getproperty;
import static org.apache.flex.abc.ABCConstants.OP_getscopeobject;
import static org.apache.flex.abc.ABCConstants.OP_getslot;
import static org.apache.flex.abc.ABCConstants.OP_getsuper;
import static org.apache.flex.abc.ABCConstants.OP_hasnext2;
import static org.apache.flex.abc.ABCConstants.OP_inclocal;
import static org.apache.flex.abc.ABCConstants.OP_inclocal_i;
import static org.apache.flex.abc.ABCConstants.OP_initproperty;
import static org.apache.flex.abc.ABCConstants.OP_istype;
import static org.apache.flex.abc.ABCConstants.OP_kill;
import static org.apache.flex.abc.ABCConstants.OP_lookupswitch;
import static org.apache.flex.abc.ABCConstants.OP_newarray;
import static org.apache.flex.abc.ABCConstants.OP_newcatch;
import static org.apache.flex.abc.ABCConstants.OP_newclass;
import static org.apache.flex.abc.ABCConstants.OP_newfunction;
import static org.apache.flex.abc.ABCConstants.OP_newobject;
import static org.apache.flex.abc.ABCConstants.OP_pushbyte;
import static org.apache.flex.abc.ABCConstants.OP_pushdouble;
import static org.apache.flex.abc.ABCConstants.OP_pushint;
import static org.apache.flex.abc.ABCConstants.OP_pushnamespace;
import static org.apache.flex.abc.ABCConstants.OP_pushscope;
import static org.apache.flex.abc.ABCConstants.OP_pushshort;
import static org.apache.flex.abc.ABCConstants.OP_pushstring;
import static org.apache.flex.abc.ABCConstants.OP_pushuint;
import static org.apache.flex.abc.ABCConstants.OP_returnvoid;
import static org.apache.flex.abc.ABCConstants.OP_setglobalslot;
import static org.apache.flex.abc.ABCConstants.OP_setlocal;
import static org.apache.flex.abc.ABCConstants.OP_setproperty;
import static org.apache.flex.abc.ABCConstants.OP_setslot;
import static org.apache.flex.abc.ABCConstants.OP_setsuper;
import static org.apache.flex.abc.ABCConstants.TRAIT_Class;
import static org.apache.flex.abc.ABCConstants.TRAIT_Const;
import static org.apache.flex.abc.ABCConstants.TRAIT_Function;
import static org.apache.flex.abc.ABCConstants.TRAIT_Getter;
import static org.apache.flex.abc.ABCConstants.TRAIT_Method;
import static org.apache.flex.abc.ABCConstants.TRAIT_Setter;
import static org.apache.flex.abc.ABCConstants.TRAIT_Var;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.flex.abc.graph.IBasicBlock;
import org.apache.flex.abc.instructionlist.InstructionList;
import org.apache.flex.abc.semantics.ClassInfo;
import org.apache.flex.abc.semantics.ExceptionInfo;
import org.apache.flex.abc.semantics.InstanceInfo;
import org.apache.flex.abc.semantics.Instruction;
import org.apache.flex.abc.semantics.Label;
import org.apache.flex.abc.semantics.Metadata;
import org.apache.flex.abc.semantics.MethodBodyInfo;
import org.apache.flex.abc.semantics.MethodInfo;
import org.apache.flex.abc.semantics.Name;
import org.apache.flex.abc.semantics.Namespace;
import org.apache.flex.abc.semantics.Nsset;
import org.apache.flex.abc.semantics.PooledValue;
import org.apache.flex.abc.semantics.ScriptInfo;
import org.apache.flex.abc.semantics.Trait;
import org.apache.flex.abc.semantics.Traits;
import org.apache.flex.abc.visitors.IABCVisitor;
import org.apache.flex.abc.visitors.IClassVisitor;
import org.apache.flex.abc.visitors.IDiagnosticsVisitor;
import org.apache.flex.abc.visitors.IMetadataVisitor;
import org.apache.flex.abc.visitors.IMethodBodyVisitor;
import org.apache.flex.abc.visitors.IMethodVisitor;
import org.apache.flex.abc.visitors.IScriptVisitor;
import org.apache.flex.abc.visitors.ITraitVisitor;
import org.apache.flex.abc.visitors.ITraitsVisitor;
import org.apache.flex.abc.visitors.NilVisitors;
/**
* The ABCEmitter is an IABCVisitor that collects information about
* the ABC and emits it as a byte array.
*/
public final class ABCEmitter implements IABCVisitor
{
private static final int VERSION_NONE = -1;
// Size of a S24 branch offset.
private static final int SIZEOF_S24 = 3;
/**
* Construct a new ABCEmitter, using the default
* diagnostics interface which ignores diagnostics.
*/
public ABCEmitter()
{
this(NilVisitors.NIL_DIAGNOSTICS_VISITOR);
}
/**
* Construct a new ABCEmitter using the specified diagnostics interface.
* @param diagnosticsVisitor - a sink for diagnostics.
*/
public ABCEmitter(IDiagnosticsVisitor diagnosticsVisitor)
{
this.w = new ABCWriter();
this.lock = new ReentrantLock();
this.visitEndCalled = false;
this.diagnosticsVisitor = diagnosticsVisitor;
}
private ABCWriter w;
/**
* Name pool, has default zero entry.
*/
final Pool<Name> namePool = new Pool<Name>(Pool.DefaultType.HasDefaultZero);
/**
* String pool, has default zero entry.
*/
final Pool<String> stringPool = new Pool<String>(Pool.DefaultType.HasDefaultZero);
/**
* int pool, has default zero entry.
*/
final Pool<Integer> intPool = new Pool<Integer>(Pool.DefaultType.HasDefaultZero);
/**
* uint pool, has default zero entry.
*/
final Pool<Long> uintPool = new Pool<Long>(Pool.DefaultType.HasDefaultZero);
/**
* double pool, has default zero entry.
*/
final Pool<Double> doublePool = new Pool<Double>(Pool.DefaultType.HasDefaultZero);
/**
* namespace pool, has default zero entry.
*/
final Pool<Namespace> nsPool = new Pool<Namespace>(Pool.DefaultType.HasDefaultZero);
/**
* namespace set pool, has default zero entry.
*/
final Pool<Nsset> nssetPool = new Pool<Nsset>(Pool.DefaultType.HasDefaultZero);
/**
* metadata pool, does not have default zero entry.
*/
final Pool<Metadata> metadataPool = new Pool<Metadata>(Pool.DefaultType.NoDefaultZero);
/**
* Visitors of classes defined in this ABC.
*/
private Collection<EmitterClassVisitor> definedClasses = new ArrayList<EmitterClassVisitor>();
/**
* MethodInfos defined in this ABC. These are stored in a dual-indexed store
* so the method info number of a MethodInfo can be quickly retrieved.
*/
private final EntryOrderedStore<MethodInfo> methodInfos = new EntryOrderedStore<MethodInfo>();
/**
* Lock used to protect the method info pool stored in the
* {@link #methodInfos} field.
*/
private final ReadWriteLock methodInfosLock = new ReentrantReadWriteLock();
/**
* Method bodies defined in this ABC.
*/
private final Vector<MethodBodyInfo> methodBodies = new Vector<MethodBodyInfo>();
/**
* Scripts defined in this ABC. There is normally one.
*/
private final Vector<ScriptInfo> scriptInfos = new Vector<ScriptInfo>();
private int versionABCMajor = VERSION_NONE;
private int versionABCMinor = VERSION_NONE;
/**
* determines whether the ABCEmitter should throw exceptions when it
* encounters jumps to nowhere, or just try and repair and keep going.
*/
private boolean allowBadJumps = false;
/**
* Should the emitter serialize method bodies to ABC as soon as possible, or
* serialize them as it emits the final ABC?
*/
private boolean eagerlyEmitMethodBodies = true;
/**
* Lock used to enforce concurrency rules of this class.
* <p>
* This lock is used to ensure that vistEnd calls are only made on the same
* thread that called {@link #visit(int, int)}.
* <p>
* When running with asserts enabled this lock is used to cause an assertion
* failure when the currency rules of this class are violated.
* <p>
* When running with asserts disabled this lock is used to cause a deadlock
* when the currency rules of this class are violated.
*
* @see #assertLockHeld()
*/
private final ReentrantLock lock;
/**
* Flag set in visitEnd(), checked in verifyEmitterStatus()
*/
private boolean visitEndCalled;
private final IDiagnosticsVisitor diagnosticsVisitor;
/**
* Emit bytecode.
* @return the ABC bytecode corresponding to the ABC structures
* visited by this emitter or its subsidiary visitors.
* @throws Exception if the emitter is unable to generate valid bytecode.
*/
public byte[] emit() throws Exception
{
if (getMajorVersion() == VERSION_NONE || getMinorVersion() == VERSION_NONE)
throw new IllegalStateException("No abc version specified");
// First sort the classes into dependency order.
this.definedClasses = ClassDependencySort.getSorted(this.definedClasses);
w.writeU16(getMinorVersion());
w.writeU16(getMajorVersion());
w.writeU30(this.intPool.getNominalSize());
for (int x : this.intPool.getValues())
{
w.writeU30(x);
}
w.writeU30(this.uintPool.getNominalSize());
for (long x : this.uintPool.getValues())
{
w.writeU30((int)x);
}
w.writeU30(this.doublePool.getNominalSize());
for (double x : this.doublePool.getValues())
{
w.write64(Double.doubleToLongBits(x));
}
w.writeU30(this.stringPool.getNominalSize());
for (String s : this.stringPool.getValues())
{
byte[] stringBytes = s.getBytes("UTF-8");
w.writeU30(stringBytes.length);
w.write(stringBytes);
}
w.writeU30(this.nsPool.getNominalSize());
for (Namespace ns : this.nsPool.getValues())
{
emitNamespace(ns);
}
w.writeU30(this.nssetPool.getNominalSize());
for (Nsset nsset : this.nssetPool.getValues())
{
w.writeU30(nsset.length());
for (Namespace ns : nsset)
{
w.writeU30(this.nsPool.id(ns));
}
}
w.writeU30(this.namePool.getNominalSize());
for (Name n : this.namePool.getValues())
{
w.write(n.getKind());
switch (n.getKind())
{
case CONSTANT_Qname:
case CONSTANT_QnameA:
{
w.writeU30(this.nsPool.id(n.getSingleQualifier()));
w.writeU30(this.stringPool.id(n.getBaseName()));
break;
}
case CONSTANT_Multiname:
case CONSTANT_MultinameA:
{
w.writeU30(this.stringPool.id(n.getBaseName()));
w.writeU30(this.nssetPool.id(n.getQualifiers()));
break;
}
case CONSTANT_RTQname:
case CONSTANT_RTQnameA:
{
w.writeU30(this.stringPool.id(n.getBaseName()));
break;
}
case CONSTANT_MultinameL:
case CONSTANT_MultinameLA:
{
w.writeU30(this.nssetPool.id(n.getQualifiers()));
break;
}
case CONSTANT_RTQnameL:
case CONSTANT_RTQnameLA:
{
break;
}
case CONSTANT_TypeName:
{
w.writeU30(this.namePool.id(n.getTypeNameBase()));
w.writeU30(1); // only 1 type parameter is currently supposed in AVM2
w.writeU30(this.namePool.id(n.getTypeNameParameter()));
break;
}
default:
{
assert (false) : "Unimplemented name kind " + n.getKind();
throw new IllegalArgumentException("Not implemented.");
}
}
}
// See the comment in EmitterMethodInfoVisitor.visit
// to understand why we have this lock here.
// The short answer is because multiple threads
// need to simultaneously add method infos to the method
// info pool.
final Lock methodInfosReadLock = methodInfosLock.readLock();
methodInfosReadLock.lock();
try
{
w.writeU30(this.methodInfos.size());
for (MethodInfo mi : this.methodInfos)
emitMethodInfo(mi);
}
finally
{
methodInfosReadLock.unlock();
}
w.writeU30(this.metadataPool.getNominalSize());
for (Metadata md : this.metadataPool.values)
{
// name
w.writeU30(this.stringPool.id(md.getName()));
// items count
assert md.getKeys().length == md.getValues().length;
w.writeU30(md.getKeys().length);
// metadata keys
for (final String key : md.getKeys())
{
final int string_index = stringPool.id(key);
w.writeU30(string_index);
}
// metadata values
for (final String value : md.getValues())
{
final int string_index = stringPool.id(value);
w.writeU30(string_index);
}
}
w.writeU30(this.definedClasses.size());
for (EmitterClassVisitor clz : this.definedClasses)
{
InstanceInfo ii = clz.instanceInfo;
w.writeU30(namePool.id(ii.name));
w.writeU30(namePool.id(ii.superName));
w.write(ii.flags);
if (ii.hasProtectedNs())
w.writeU30(this.nsPool.id(ii.protectedNs));
w.writeU30(ii.interfaceNames.length);
for (Name i : ii.interfaceNames)
w.writeU30(this.namePool.id(i));
w.writeU30(getMethodId(ii.iInit));
emitTraits(clz.instanceTraits);
}
for (EmitterClassVisitor clz : this.definedClasses)
{
w.writeU30(getMethodId(clz.classInfo.cInit));
emitTraits(clz.classTraits);
}
w.writeU30(this.scriptInfos.size());
for (ScriptInfo s : this.scriptInfos)
{
emitScriptInfo(s);
}
w.writeU30(this.methodBodies.size());
for (MethodBodyInfo mb : this.methodBodies)
{
emitMethodBody(mb);
}
return w.getDirectByteArray();
}
private int getMajorVersion()
{
return versionABCMajor;
}
private int getMinorVersion()
{
return versionABCMinor;
}
/**
* Emit ABC bytes for a Traits entry.
*
* @param traits - the Traits to emit.
* @see #poolTraitValues(Traits) which puts these values into the pools.
*/
private void emitTraits(Traits traits)
{
w.writeU30(traits.getTraitCount());
for (Trait t : traits)
{
w.writeU30(namePool.id(t.getNameAttr("name")));
// Get the kind byte with its flags set in the high nibble.
w.write(t.getFullKindByte());
switch (t.getKind())
{
case TRAIT_Var:
case TRAIT_Const:
{
w.writeU30(t.getIntAttr(Trait.TRAIT_SLOT));
w.writeU30(namePool.id(t.getNameAttr(Trait.TRAIT_TYPE)));
// Emit the Trait's initial value, if it has one.
Object trait_value = t.getAttr(Trait.SLOT_VALUE);
if (trait_value != null)
{
if (trait_value instanceof String)
{
w.writeU30(stringPool.id((String)trait_value));
w.write(ABCConstants.CONSTANT_Utf8);
}
else if (trait_value instanceof Namespace)
{
w.writeU30(nsPool.id((Namespace)trait_value));
w.write(ABCConstants.CONSTANT_Namespace);
}
else if (trait_value instanceof Double)
{
w.writeU30(doublePool.id((Double)trait_value));
w.write(ABCConstants.CONSTANT_Double);
}
else if (trait_value instanceof Integer)
{
w.writeU30(intPool.id((Integer)trait_value));
w.write(ABCConstants.CONSTANT_Int);
}
else if (trait_value instanceof Long)
{
w.writeU30(uintPool.id((Long)trait_value));
w.write(ABCConstants.CONSTANT_UInt);
}
else if (trait_value.equals(Boolean.TRUE))
{
w.writeU30(ABCConstants.CONSTANT_True);
w.write(ABCConstants.CONSTANT_True);
}
else if (trait_value.equals(Boolean.FALSE))
{
w.writeU30(ABCConstants.CONSTANT_False);
w.write(ABCConstants.CONSTANT_False);
}
else if (trait_value == ABCConstants.NULL_VALUE)
{
w.writeU30(ABCConstants.CONSTANT_Null);
w.write(ABCConstants.CONSTANT_Null);
}
else if (trait_value == ABCConstants.UNDEFINED_VALUE)
{
// Undefined is a special case; it has no kind byte.
w.writeU30(0);
}
else
{
throw new IllegalStateException("Unrecognized initializer type: " + trait_value.getClass().toString());
}
}
else
{
w.writeU30(0);
}
break;
}
case TRAIT_Method:
case TRAIT_Function:
case TRAIT_Getter:
case TRAIT_Setter:
{
if (t.hasAttr(Trait.TRAIT_DISP))
w.writeU30(t.getIntAttr(Trait.TRAIT_DISP));
else
w.writeU30(0);
w.writeU30(this.getMethodId((MethodInfo)t.getAttr(Trait.TRAIT_METHOD)));
break;
}
case TRAIT_Class:
{
if (t.hasAttr(Trait.TRAIT_SLOT))
w.writeU30(t.getIntAttr(Trait.TRAIT_SLOT));
else
w.writeU30(0);
w.writeU30(this.getClassId(((ClassInfo)t.getAttr(Trait.TRAIT_CLASS))));
break;
}
default:
{
throw new IllegalArgumentException("Unknown trait kind " + t.getKind());
}
}
if (t.hasMetadata())
{
Vector<Metadata> metadata = t.getMetadata();
w.writeU30(metadata.size());
for (Metadata m : metadata)
{
w.writeU30(metadataPool.id(m));
}
}
}
}
private int getMethodId(MethodInfo info)
{
Lock methodInfosReadLock = methodInfosLock.readLock();
methodInfosReadLock.lock();
try
{
return methodInfos.getId(info);
}
finally
{
methodInfosReadLock.unlock();
}
}
/**
* @return the class ID of the given ClassInfo.
* @throws IllegalArgumentException if the class is not found.
*/
private int getClassId(ClassInfo info)
{
int id_index = 0;
for (EmitterClassVisitor candidate : this.definedClasses)
{
if (candidate.classInfo == info)
return id_index;
else
id_index++;
}
throw new IllegalArgumentException("Unable to find ClassInfo index for " + info);
}
private void emitScriptInfo(ScriptInfo info)
{
final MethodInfo scriptInit = info.getInit();
final int nRequiredArguments =
scriptInit.getParamTypes().size() - scriptInit.getDefaultValues().size();
if (nRequiredArguments > 0)
diagnosticsVisitor.scriptInitWithRequiredArguments(info, scriptInit);
w.writeU30(getMethodId(scriptInit));
emitTraits(info.getTraits());
}
private void emitMethodInfo(MethodInfo info)
{
final Collection<Name> paramTypes = info.getParamTypes();
final int nParamTypes = paramTypes.size();
w.writeU30(nParamTypes);
w.writeU30(namePool.id(info.getReturnType()));
for (Name n : paramTypes)
{
w.writeU30(namePool.id(n));
}
w.writeU30(this.stringPool.id(info.getMethodName()));
w.write(info.getFlags());
if (info.hasOptional())
{
Collection<PooledValue> defaults = info.getDefaultValues();
final int nDefaults = defaults.size();
if (nDefaults > nParamTypes)
this.diagnosticsVisitor.tooManyDefaultParameters(info);
w.writeU30(nDefaults);
for (PooledValue v : defaults)
{
w.writeU30(v.getPoolIndex());
w.write(v.getKind());
}
}
if (info.hasParamNames())
{
List<String> paramNames = info.getParamNames();
final int nParamNames = paramNames.size();
if (nParamTypes != nParamNames)
this.diagnosticsVisitor.incorrectNumberOfParameterNames(info);
for (String param_name : info.getParamNames())
w.writeU30(stringPool.id(param_name));
}
}
private void emitMethodBody(MethodBodyInfo f) throws Exception
{
MethodInfo signature = f.getMethodInfo();
w.writeU30(getMethodId(signature));
// Note: computeFrameCounts() called at MethodBodyInfo.visitEnd().
w.writeU30(f.getMaxStack());
int max_local = f.getLocalCount();
if (signature.getParamCount() > max_local)
max_local = signature.getParamCount();
w.writeU30(max_local);
w.writeU30(f.getInitScopeDepth());
w.writeU30(f.getMaxScopeDepth());
if (!f.hasBytecode())
emitCode(f);
w.write(f.getBytecode());
emitTraits(f.getTraits());
}
private void emitCode(MethodBodyInfo f) throws Exception
{
ABCWriter result = new ABCWriter();
Map<IBasicBlock, ABCWriter> writers = new HashMap<IBasicBlock, ABCWriter>();
// generate linear code sequences within the basic blocks,
// and compute the offset of each block from the method start.
Map<IBasicBlock, Integer> block_offsets = new HashMap<IBasicBlock, Integer>();
int code_len = 0;
// First, generate code for each block.
// Keep a running tally of the code length,
// which corresponds to the starting position
// of each block.
for (IBasicBlock b : f.getCfg().getBlocksInEntryOrder())
{
block_offsets.put(b, code_len);
ABCWriter blockWriter = new ABCWriter();
writers.put(b, blockWriter);
emitBlock(b, blockWriter);
code_len += blockWriter.size();
// Blocks with no instructions are
// valid assembly constructs.
if (b.size() == 0)
continue;
// If the last instruction in the block
// is a jump, leave room for the instruction,
// but don't emit it yet.
Instruction last = b.get(b.size() - 1);
if (last.isBranch())
{
if (last.getOpcode() == OP_lookupswitch)
{
// Switch table contains a U30 case count and S24 offsets.
int switch_size = 1 + sizeOfU30(last.getOperandCount()) + SIZEOF_S24 * last.getOperandCount();
code_len += switch_size;
}
else
{
assert (null != last.getTarget());
// Reserve space for a branch instruction.
code_len += 4;
}
}
}
// Note: Can't compute code_start until we've seen
// how big this U30 is.
result.writeU30(code_len);
int code_start = result.size();
// Now we can resolve labels to their code offsets.
// Copy the linear code sequences into the main ABCWriter,
// and emit the branch and lookupswitch instructions.
for (IBasicBlock b : f.getCfg().getBlocksInEntryOrder())
{
writers.get(b).writeTo(result);
if (b.size() > 0)
{
Instruction last = b.get(b.size() - 1);
if (last.isBranch())
{
if (OP_lookupswitch == last.getOpcode())
{
emitLookupswitch(result, code_start, f, last, block_offsets);
}
else
{
assert (last.getTarget() != null);
emitBranch(result, last.getOpcode(), f.getBlock(last.getTarget(), !allowBadJumps), code_start, block_offsets, code_len);
}
}
}
}
emitExceptionInfo(f, result, block_offsets);
f.setBytecode(result.getDirectByteArray());
}
private void emitBranch(ABCWriter writer, int opcode, IBasicBlock target, int code_start, Map<IBasicBlock, Integer> block_offsets, int code_len)
{
writer.write(opcode);
// Branch offset computed from the instruction following the branch.
int from = writer.size() + SIZEOF_S24;
// Convert the target offset relative to the start of the ABC, as
// the "from" address is expressed in this fashion.
// if we can't determine the target then jump past the end of the method - this is to allow
// malformed ABCs to be successfully processed by AET.
int to = code_start + (target != null ? block_offsets.get(target) : code_len + 1);
writer.writeS24(to - from);
}
void emitBlock(IBasicBlock b, ABCWriter blockWriter)
{
for (int i = 0; i < b.size() && !b.get(i).isBranch(); i++)
{
Instruction insn = b.get(i);
blockWriter.write(insn.getOpcode());
switch (insn.getOpcode())
{
case OP_hasnext2:
{
blockWriter.writeU30((Integer)insn.getOperand(0));
blockWriter.writeU30((Integer)insn.getOperand(1));
break;
}
case OP_findproperty:
case OP_findpropstrict:
case OP_getlex:
case OP_getsuper:
case OP_setsuper:
case OP_deleteproperty:
case OP_getdescendants:
case OP_getproperty:
case OP_setproperty:
case OP_initproperty:
case OP_istype:
case OP_coerce:
case OP_astype:
case OP_finddef:
{
blockWriter.writeU30(namePool.id((Name)insn.getOperand(0)));
break;
}
case OP_callproperty:
case OP_callproplex:
case OP_callpropvoid:
case OP_callsuper:
case OP_callsupervoid:
case OP_constructprop:
{
blockWriter.writeU30(namePool.id((Name)insn.getOperand(0)));
blockWriter.writeU30((Integer)insn.getOperand(1));
break;
}
case OP_constructsuper:
case OP_call:
case OP_construct:
case OP_newarray:
case OP_newobject:
case OP_getlocal:
case OP_setlocal:
case OP_getslot:
case OP_setslot:
case OP_kill:
case OP_inclocal:
case OP_declocal:
case OP_inclocal_i:
case OP_declocal_i:
case OP_newcatch:
case OP_getglobalslot:
case OP_setglobalslot:
case OP_applytype:
case OP_getscopeobject:
case OP_pushshort:
{
blockWriter.writeU30(insn.getImmediate());
break;
}
case OP_pushbyte:
{
blockWriter.write(insn.getImmediate());
break;
}
case OP_newclass:
{
blockWriter.writeU30(getClassId((ClassInfo)insn.getOperand(0)));
break;
}
case OP_newfunction:
{
blockWriter.writeU30(getMethodId((MethodInfo)insn.getOperand(0)));
break;
}
case OP_callstatic:
{
blockWriter.writeU30(getMethodId((MethodInfo)insn.getOperand(0)));
blockWriter.writeU30((Integer)(insn.getOperand(1)));
break;
}
case OP_pushstring:
case OP_dxns:
case OP_debugfile:
{
blockWriter.writeU30(stringPool.id(insn.getOperand(0).toString()));
break;
}
case OP_pushnamespace:
{
blockWriter.writeU30(nsPool.id((Namespace)insn.getOperand(0)));
break;
}
case OP_pushint:
{
blockWriter.writeU30(intPool.id((Integer)insn.getOperand(0)));
break;
}
case OP_pushuint:
{
blockWriter.writeU30(uintPool.id((Long)insn.getOperand(0)));
break;
}
case OP_pushdouble:
{
blockWriter.writeU30(doublePool.id((Double)insn.getOperand(0)));
break;
}
case OP_debugline:
case OP_bkptline:
{
blockWriter.writeU30(insn.getImmediate());
break;
}
case OP_debug:
{
blockWriter.write((Integer)(insn.getOperand(0)));
blockWriter.writeU30(stringPool.id(insn.getOperand(1).toString()));
blockWriter.write((Integer)insn.getOperand(2));
blockWriter.writeU30(0);
break;
}
}
}
}
private void emitNamespace(Namespace ns)
{
w.write(ns.getKind());
w.writeU30(stringPool.id(ns.getVersionedName()));
}
private void emitExceptionInfo(MethodBodyInfo f, ABCWriter w, Map<IBasicBlock, Integer> pos)
{
w.writeU30(f.getExceptions().size());
for (ExceptionInfo ex : f.getExceptions())
{
if ( ex.isLive() )
{
w.writeU30(pos.get(f.getBlock(ex.getFrom())));
w.writeU30(pos.get(f.getBlock(ex.getTo())));
w.writeU30(pos.get(f.getBlock(ex.getTarget())));
w.writeU30(namePool.id(ex.getExceptionType()));
w.writeU30(namePool.id(ex.getCatchVar()));
}
}
}
void emitLookupswitch(ABCWriter out, int code_start, MethodBodyInfo f,
Instruction switch_insn, Map<IBasicBlock, Integer> block_offsets)
{
int case_size = switch_insn.getOperandCount() - 1; // Last Label reserved for the default
// "The base location is the address of the lookupswitch instruction itself." - AVM2
int base_loc = out.size() - code_start;
out.write(OP_lookupswitch);
// The last label is the default case.
Label default_case = (Label)switch_insn.getOperand(case_size);
int default_offset = (block_offsets.get(f.getBlock(default_case)) - base_loc);
out.writeS24(default_offset);
out.writeU30(case_size - 1);
for (int i = 0; i < case_size; i++)
{
int branch_offset = (block_offsets.get(f.getBlock((Label)switch_insn.getOperand(i))) - base_loc);
out.writeS24(branch_offset);
}
}
static class ABCWriter extends ByteArrayOutputStream
{
void rewind(int n)
{
super.count -= n;
}
void writeU16(int i)
{
write(i);
write(i >> 8);
}
void writeS24(int i)
{
writeU16(i);
write(i >> 16);
}
void write64(long i)
{
writeS24((int)i);
writeS24((int)(i >> 24));
writeU16((int)(i >> 48));
}
void writeU30(int v)
{
if (v < 128 && v >= 0)
{
write(v);
}
else if (v < 16384 && v >= 0)
{
write(v & 0x7F | 0x80);
write(v >> 7);
}
else if (v < 2097152 && v >= 0)
{
write(v & 0x7F | 0x80);
write(v >> 7 | 0x80);
write(v >> 14);
}
else if (v < 268435456 && v >= 0)
{
write(v & 0x7F | 0x80);
write(v >> 7 | 0x80);
write(v >> 14 | 0x80);
write(v >> 21);
}
else
{
write(v & 0x7F | 0x80);
write(v >> 7 | 0x80);
write(v >> 14 | 0x80);
write(v >> 21 | 0x80);
write(v >>> 28);
}
}
int sizeOfU30(int v)
{
if (v < 128 && v >= 0)
return 1;
else if (v < 16384 && v >= 0)
return 2;
else if (v < 2097152 && v >= 0)
return 3;
else if (v < 268435456 && v >= 0)
return 4;
else
return 5;
}
/**
* Get the byte array contained within this stream. Note: flush may need to be called
* @return the byte array
*/
public byte[] getDirectByteArray()
{
// only return the buffer directly if the length of the buffer
// is the same as the count, as the buffer can be bigger than the
// count, and in this case, we return an array which is larger than
// expected, so we need to truncate it with a copy.
if (buf.length == count)
return buf;
else
return super.toByteArray();
}
/**
* Ensure toByteArray() is never called.
*/
@Override
public byte[] toByteArray()
{
throw new UnsupportedOperationException();
}
}
/**
* Find all the operands in a method body and make sure they find their way
* into the appropriate pool.
*
* @param mi - the method body.
* @post any runtime multinames have a dummy Name operand.
*/
void poolOperands(MethodBodyInfo mbi)
{
for (IBasicBlock b : mbi.getCfg().getBlocksInEntryOrder())
{
for (Instruction insn : b.getInstructions())
{
switch (insn.getOpcode())
{
case OP_findproperty:
case OP_findpropstrict:
case OP_getlex:
case OP_getdescendants:
case OP_initproperty:
case OP_istype:
case OP_coerce:
case OP_astype:
case OP_finddef:
case OP_deleteproperty:
case OP_getproperty:
case OP_setproperty:
case OP_getsuper:
case OP_setsuper:
{
visitPooledName((Name)insn.getOperand(0));
break;
}
case OP_callproperty:
case OP_callproplex:
case OP_callpropvoid:
case OP_callsuper:
case OP_callsupervoid:
case OP_constructprop:
{
visitPooledName((Name)insn.getOperand(0));
break;
}
case OP_pushstring:
case OP_dxns:
case OP_debugfile:
{
stringPool.add(insn.getOperand(0).toString());
break;
}
case OP_pushnamespace:
{
visitPooledNamespace((Namespace)insn.getOperand(0));
break;
}
case OP_pushint:
{
intPool.add((Integer)insn.getOperand(0));
break;
}
case OP_pushuint:
{
uintPool.add((Long)insn.getOperand(0));
break;
}
case OP_pushdouble:
{
doublePool.add((Double)insn.getOperand(0));
break;
}
case OP_debug:
{
stringPool.add(insn.getOperand(1).toString());
break;
}
}
}
}
}
/**
* Pool all the objects referenced by a Traits that need to be added to the
* various constant pools. These objects include:
* <ul>
* <li>The {@link Name} object for the qualified name of each {@link Trait}.
* </li>
* <li>The {@link Name} object that refers to the class or interface of each
* {@link Trait} with a type annotation.</li>
* <li>The {@link Metadata} objects that decorate each {@link Trait}.</li>
* <li>The {@link String}, {@link Namespace}, {@link Double},
* {@link Integer}, or {@link Long} objects that specify the
* initial value of each {@link Trait}.</li>
* </ul>
*/
private void poolTraitsConstants(Traits ts)
{
for (Trait t : ts)
{
Name traitName = t.getNameAttr(Trait.TRAIT_NAME);
visitPooledName(traitName);
if (t.hasAttr(Trait.TRAIT_TYPE))
visitPooledName(t.getNameAttr(Trait.TRAIT_TYPE));
for (Metadata md : t.getMetadata())
visitPooledMetadata(md);
if (t.hasAttr(Trait.SLOT_VALUE))
{
Object trait_value = t.getAttr(Trait.SLOT_VALUE);
if (trait_value == null)
{
// No action required; the pool ID resolution logic
// handles a null pointer by returning the nil pool value.
}
else if (trait_value instanceof String)
{
visitPooledString((String)trait_value);
}
else if (trait_value instanceof Namespace)
{
visitPooledNamespace((Namespace)trait_value);
}
else if (trait_value instanceof Double)
{
visitPooledDouble((Double)trait_value);
}
else if (trait_value instanceof Integer)
{
visitPooledInt((Integer)trait_value);
}
else if (trait_value instanceof Long)
{
visitPooledUInt((Long)trait_value);
}
else if (trait_value.equals(ABCConstants.UNDEFINED_VALUE)
|| trait_value.equals(ABCConstants.NULL_VALUE)
|| trait_value.equals(Boolean.TRUE)
|| trait_value.equals(Boolean.FALSE))
{
// Nothing to do, predefined value.
}
else
{
throw new IllegalStateException("Unrecognized initializer type: " + trait_value.getClass().toString());
}
}
}
}
@Override
public void visit(int majorVersion, int minorVersion)
{
verifyEmitterStatus();
this.lock.lock();
assert this.lock.getHoldCount() == 1 : "The hold count should be 1, beacuse this method should only be called once!";
if (versionABCMajor == VERSION_NONE)
{
versionABCMajor = majorVersion;
versionABCMinor = minorVersion;
}
else if (versionABCMajor != majorVersion || versionABCMinor != minorVersion)
{
throw new IllegalArgumentException("abc versions do not match");
}
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
this.visitEndCalled = true;
}
@Override
public IClassVisitor visitClass(InstanceInfo iinfo, ClassInfo cinfo)
{
verifyEmitterStatus();
EmitterClassVisitor result = new EmitterClassVisitor(iinfo, cinfo);
result.visit();
return result;
}
@Override
public IScriptVisitor visitScript()
{
verifyEmitterStatus();
return new EmitterScriptInfo();
}
@Override
public IMethodVisitor visitMethod(MethodInfo minfo)
{
verifyEmitterStatus();
return new EmitterMethodInfoVisitor(minfo);
}
@Override
public void visitPooledDouble(Double d)
{
verifyEmitterStatus();
this.doublePool.add(d);
}
@Override
public void visitPooledInt(Integer i)
{
verifyEmitterStatus();
this.intPool.add(i);
}
@Override
public void visitPooledMetadata(Metadata md)
{
verifyEmitterStatus();
this.metadataPool.add(md);
visitPooledString(md.getName());
for (String key : md.getKeys())
visitPooledString(key);
for (String value : md.getValues())
visitPooledString(value);
}
@Override
public void visitPooledName(Name n)
{
verifyEmitterStatus();
if (null == n)
return;
final int kind = n.getKind();
if (kind != ABCConstants.CONSTANT_TypeName)
{
this.namePool.add(n);
visitPooledString(n.getBaseName());
if ((kind == ABCConstants.CONSTANT_Qname) || (kind == ABCConstants.CONSTANT_QnameA))
visitPooledNamespace(n.getSingleQualifier());
else
visitPooledNsSet(n.getQualifiers());
}
else
{
visitPooledName(n.getTypeNameBase());
visitPooledName(n.getTypeNameParameter());
this.namePool.add(n);
}
}
@Override
public void visitPooledNamespace(Namespace ns)
{
verifyEmitterStatus();
this.nsPool.add(ns);
if (ns != null)
visitPooledString(ns.getVersionedName());
}
@Override
public void visitPooledNsSet(Nsset nss)
{
verifyEmitterStatus();
this.nssetPool.add(nss);
if (nss != null)
{
for (Namespace ns : nss)
{
visitPooledNamespace(ns);
}
}
}
@Override
public void visitPooledString(String s)
{
verifyEmitterStatus();
this.stringPool.add(s);
}
@Override
public void visitPooledUInt(Long l)
{
verifyEmitterStatus();
this.uintPool.add(l);
}
public static int sizeOfU30(int v)
{
if (v < 128 && v >= 0)
return 1;
else if (v < 16384 && v >= 0)
return 2;
else if (v < 2097152 && v >= 0)
return 3;
else if (v < 268435456 && v >= 0)
return 4;
else
return 5;
}
private class EmitterClassVisitor implements IClassVisitor, ClassDependencySort.IInstanceInfoProvider
{
EmitterClassVisitor(InstanceInfo iinfo, ClassInfo cinfo)
{
this.classInfo = cinfo;
if (null == cinfo.classTraits)
cinfo.classTraits = new Traits();
this.classTraits = cinfo.classTraits;
this.instanceInfo = iinfo;
if (null == iinfo.traits)
iinfo.traits = new Traits();
this.instanceTraits = iinfo.traits;
if (null == iinfo.interfaceNames)
iinfo.interfaceNames = new Name[0];
}
ClassInfo classInfo;
Traits classTraits;
InstanceInfo instanceInfo;
Traits instanceTraits;
@Override
public void visit()
{
verifyEmitterStatus();
}
@Override
public ITraitsVisitor visitClassTraits()
{
verifyEmitterStatus();
return new EmitterTraitsVisitor(this.classTraits);
}
@Override
public ITraitsVisitor visitInstanceTraits()
{
verifyEmitterStatus();
return new EmitterTraitsVisitor(this.instanceTraits);
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
definedClasses.add(this);
if (null == classInfo.cInit)
{
classInfo.cInit = new MethodInfo();
MethodBodyInfo m_cinit = new MethodBodyInfo();
m_cinit.setMethodInfo(classInfo.cInit);
IMethodVisitor mv = visitMethod(classInfo.cInit);
mv.visit();
IMethodBodyVisitor mbv = mv.visitBody(m_cinit);
mbv.visit();
mbv.visitInstruction(OP_returnvoid);
mbv.visitEnd();
mv.visitEnd();
}
visitPooledName(instanceInfo.name);
visitPooledName(instanceInfo.superName);
if (instanceInfo.hasProtectedNs())
visitPooledNamespace(instanceInfo.protectedNs);
if (null == instanceInfo.iInit)
{
instanceInfo.iInit = new MethodInfo();
MethodBodyInfo iinit = new MethodBodyInfo();
iinit.setMethodInfo(instanceInfo.iInit);
IMethodVisitor mv = visitMethod(instanceInfo.iInit);
mv.visit();
// Interfaces need an instance init method,
// but it doesn't have a body.
if (0 == (instanceInfo.flags & ABCConstants.CLASS_FLAG_interface))
{
IMethodBodyVisitor mbv = mv.visitBody(iinit);
mbv.visit();
mbv.visitInstruction(OP_getlocal0);
mbv.visitInstruction(OP_pushscope);
mbv.visitInstruction(OP_getlocal0);
mbv.visitInstruction(ABCConstants.OP_constructsuper, 0);
mbv.visitInstruction(OP_returnvoid);
mbv.visitEnd();
}
mv.visitEnd();
}
if (instanceInfo.interfaceNames != null)
{
for (Name interface_name : instanceInfo.interfaceNames)
{
visitPooledName(interface_name);
}
}
}
@Override
public InstanceInfo getInstanceInfo()
{
return this.instanceInfo;
}
}
private class EmitterTraitsVisitor implements ITraitsVisitor
{
EmitterTraitsVisitor(Traits traits)
{
this.traits = traits;
}
Traits traits;
@Override
public ITraitVisitor visitClassTrait(int kind, Name name, int slot_id, ClassInfo clazz)
{
verifyEmitterStatus();
Trait t = createTrait(kind, name);
if (slot_id != 0)
t.addAttr(Trait.TRAIT_SLOT, slot_id);
t.addAttr(Trait.TRAIT_CLASS, clazz);
return new EmitterTraitVisitor(t);
}
@Override
public ITraitVisitor visitMethodTrait(int kind, Name name, int dispId, MethodInfo method)
{
verifyEmitterStatus();
Trait t = createTrait(kind, name);
t.addAttr(Trait.TRAIT_METHOD, method);
if (dispId != 0)
t.addAttr(Trait.TRAIT_DISP, dispId);
return new EmitterTraitVisitor(t);
}
@Override
public ITraitVisitor visitSlotTrait(int kind, Name name, int slotId, Name slotType, Object slotValue)
{
verifyEmitterStatus();
Trait t = createTrait(kind, name);
t.addAttr(Trait.TRAIT_SLOT, slotId);
t.addAttr(Trait.TRAIT_TYPE, slotType);
t.addAttr(Trait.SLOT_VALUE, slotValue);
if (slotType != null)
visitPooledName(slotType);
return new EmitterTraitVisitor(t);
}
@Override
public void visit()
{
verifyEmitterStatus();
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
poolTraitsConstants(traits);
}
@Override
public Traits getTraits()
{
return this.traits;
}
private Trait createTrait(int kind, Name name)
{
verifyEmitterStatus();
Trait t = new Trait(kind, name);
traits.add(t);
return t;
}
}
private class EmitterTraitVisitor implements ITraitVisitor
{
EmitterTraitVisitor(Trait t)
{
this.t = t;
}
Trait t;
@Override
public IMetadataVisitor visitMetadata(int count)
{
verifyEmitterStatus();
return new IMetadataVisitor()
{
@Override
public void visit(Metadata md)
{
verifyEmitterStatus();
t.addMetadata(md);
}
};
}
@Override
public void visitAttribute(String attr_name, Object attr_value)
{
verifyEmitterStatus();
this.t.addAttr(attr_name, attr_value);
}
@Override
public void visitStart()
{
verifyEmitterStatus();
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
// We do not need to assert that the lock is held, because this method is empty,
// but if this method ever started doing anything we would not want clients to
// be broken.
assertLockHeld();
}
}
private class EmitterMethodBodyInfo implements IMethodBodyVisitor
{
EmitterMethodBodyInfo(MethodBodyInfo mbinfo)
{
this.mbi = mbinfo;
}
MethodBodyInfo mbi;
@Override
public void visit()
{
verifyEmitterStatus();
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
poolOperands(mbi);
methodBodies.add(mbi);
for (ExceptionInfo exceptionInfo : mbi.getExceptions())
{
visitPooledName(exceptionInfo.getExceptionType());
visitPooledName(exceptionInfo.getCatchVar());
}
}
@Override
public void visitInstruction(int opcode)
{
verifyEmitterStatus();
this.mbi.insn(opcode);
}
@Override
public void visitInstruction(int opcode, int immediate_operand)
{
verifyEmitterStatus();
this.mbi.insn(opcode, immediate_operand);
}
@Override
public void visitInstruction(int opcode, Object single_operand)
{
verifyEmitterStatus();
this.mbi.insn(opcode, single_operand);
}
@Override
public void visitInstruction(int opcode, Object[] operands)
{
verifyEmitterStatus();
this.mbi.insn(opcode, operands);
}
@Override
public void visitInstruction(Instruction insn)
{
verifyEmitterStatus();
this.mbi.insn(insn);
}
@Override
public ITraitsVisitor visitTraits()
{
verifyEmitterStatus();
return new EmitterTraitsVisitor(this.mbi.getTraits());
}
@Override
public int visitException(Label from, Label to, Label target, Name ex_type, Name ex_var)
{
verifyEmitterStatus();
return mbi.addExceptionInfo(new ExceptionInfo(from, to, target, ex_type, ex_var));
}
@Override
public void visitInstructionList(InstructionList new_list)
{
verifyEmitterStatus();
mbi.setInstructionList(new_list);
}
@Override
public void labelCurrent(Label l)
{
mbi.labelCurrent(l);
}
@Override
public void labelNext(Label l)
{
mbi.labelNext(l);
}
}
private class EmitterScriptInfo implements IScriptVisitor
{
EmitterScriptInfo()
{
this.si = new ScriptInfo();
}
final ScriptInfo si;
@Override
public void visit()
{
verifyEmitterStatus();
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
scriptInfos.add(this.si);
}
@Override
public void visitInit(MethodInfo init_method)
{
verifyEmitterStatus();
si.setInit(init_method);
}
@Override
public ITraitsVisitor visitTraits()
{
verifyEmitterStatus();
return new EmitterTraitsVisitor(si.getTraits());
}
}
private class EmitterMethodInfoVisitor implements IMethodVisitor
{
EmitterMethodInfoVisitor(MethodInfo mi)
{
assert (mi != null);
this.mi = mi;
}
final MethodInfo mi;
MethodBodyInfo mbi;
@Override
public void visit()
{
verifyEmitterStatus();
// We need to grab a lock before adding the method
// info to the method pool because this method ( the visit method )
// can be called from any number of threads at the same time for the
// same emitter. This is the only place where we mutate emitter
// state from a method other than visitEnd.
//
// We need to add the method info to the method pool here
// so that the EmitterMethodBodyInfo.visitEnd call for other
// methods can compute a method id for the method info
// associated with this EmitterMethodInfoVisitor before
// visitEnd has been called on this EmitterMethodInfoVisitor.
final Lock methodInfosWriteLock = methodInfosLock.writeLock();
methodInfosWriteLock.lock();
try
{
methodInfos.add(mi);
}
finally
{
methodInfosWriteLock.unlock();
}
}
@Override
public IMethodBodyVisitor visitBody(MethodBodyInfo mbi)
{
verifyEmitterStatus();
this.mbi = mbi;
return new EmitterMethodBodyInfo(mbi);
}
@Override
public void visitEnd()
{
verifyEmitterStatus();
assertLockHeld();
for (Name param_type_name : mi.getParamTypes())
visitPooledName(param_type_name);
if ((mbi != null) && (mi.isNative()))
diagnosticsVisitor.nativeMethodWithMethodBody(mi, mbi);
visitPooledString(mi.getMethodName());
visitPooledName(mi.getReturnType());
for (Name ptype : mi.getParamTypes())
{
visitPooledName(ptype);
}
if (mi.hasOptional())
{
for (PooledValue v : mi.getDefaultValues())
{
v.setPoolIndex(visitPooledValue(v));
}
}
if (mi.hasParamNames())
{
for (String param_name : mi.getParamNames())
{
visitPooledString(param_name);
}
}
// Save the method body as compressed ABC bytecode,
// if so indicated. EmitterMethodBodyInfo.visitEnd()
// does not do this because the method body needs the
// type names and default values pooled in this method.
if (this.mbi != null)
{
this.mbi.computeFrameCounts(ABCEmitter.this.diagnosticsVisitor);
// Don't eagerly emit (to ABC blobs) method bodies that
// contain newclass instructions. The class declarations
// are the one ABC header structure that may be sorted even
// without optimization (to reorder the declarations in
// dependency order). If that happens then the indices
// in the newclass instructions would be invalid.
if (eagerlyEmitMethodBodies && !this.mbi.hasNewclassInstruction())
{
try
{
emitCode(this.mbi);
}
catch (RuntimeException uncheckedSNAFU)
{
throw uncheckedSNAFU;
}
catch (Exception checkedSNAFU)
{
// TODO: The AET needs an error/warnings interface.
// For now, "report" by rethrowing wrapped in an
// unchecked exception.
throw new IllegalStateException(checkedSNAFU);
}
}
}
}
}
/**
* Record a PooledValue's value in the appropriate constant pool.
*
* @return the pool index.
* @see #PooledValue.setPoolIndex() which consumes the index.
*/
private int visitPooledValue(PooledValue value)
{
switch (value.getKind())
{
case ABCConstants.CONSTANT_Int:
{
visitPooledInt(value.getIntegerValue());
return this.intPool.id(value.getIntegerValue());
}
case ABCConstants.CONSTANT_UInt:
{
visitPooledUInt(value.getLongValue());
return this.uintPool.id(value.getLongValue());
}
case ABCConstants.CONSTANT_Double:
{
visitPooledDouble(value.getDoubleValue());
return this.doublePool.id(value.getDoubleValue());
}
case ABCConstants.CONSTANT_Utf8:
{
visitPooledString(value.getStringValue());
return this.stringPool.id(value.getStringValue());
}
// The kind and index for a manifest CONSTANT are identical.
case ABCConstants.CONSTANT_True:
{
return ABCConstants.CONSTANT_True;
}
case ABCConstants.CONSTANT_False:
{
return ABCConstants.CONSTANT_False;
}
case ABCConstants.CONSTANT_Undefined:
{
return ABCConstants.CONSTANT_Undefined;
}
case ABCConstants.CONSTANT_Null:
{
return ABCConstants.CONSTANT_Null;
}
// All these variants of namespace are stored in the same pool.
case ABCConstants.CONSTANT_Namespace:
case ABCConstants.CONSTANT_PackageNs:
case ABCConstants.CONSTANT_PackageInternalNs:
case ABCConstants.CONSTANT_ProtectedNs:
case ABCConstants.CONSTANT_ExplicitNamespace:
case ABCConstants.CONSTANT_StaticProtectedNs:
case ABCConstants.CONSTANT_PrivateNs:
{
visitPooledNamespace(value.getNamespaceValue());
return this.nsPool.id(value.getNamespaceValue());
}
default:
{
throw new IllegalStateException("Unrecognized initializer type: " + value.getKind());
}
}
}
/**
* Allow invalid jump instructions for legacy ABCs.
* @param b - true if the caller wishes to allow bad jumps.
*/
public void setAllowBadJumps(boolean b)
{
this.allowBadJumps = b;
}
/**
* Lock used to enforce concurrency rules of this class.
* <p>
* This method is used to ensure that vistEnd calls are only made on the
* same thread that called {@link #visit(int, int)}.
* <p>
* When running with asserts enabled this method will cause an assertion
* failure when the currency rules of this class are violated.
* <p>
* When running with asserts disabled this method will cause a deadlock when
* the currency rules of this class are violated.
*
* @see #lock()
*/
private void assertLockHeld()
{
assert this.lock.isHeldByCurrentThread() : "A visitEnd method was called from a thread other than the thread that called IABCVisitor.visit!";
// lock immediately followed by unlock should be a NOP, unless
// some other thread holds the lock ( which the assert above guards against when
// running with asserts enabled ).
lock.lock();
lock.unlock();
}
/**
* verifyEmitterStatus() verifies that the emitter has not seen a visitEnd()
* call.
*/
private void verifyEmitterStatus()
{
if (this.visitEndCalled)
throw new IllegalStateException("An ABCEmitter can only emit once visitEnd has been called.");
}
}