/* * * 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); public Pool<Name> getNamePool() { return namePool; } /** * String pool, has default zero entry. */ final Pool<String> stringPool = new Pool<String>(Pool.DefaultType.HasDefaultZero); public Pool<String> getStringPool() { return stringPool; } /** * 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); public Pool<Namespace> getNamespacePool() { return nsPool; } /** * 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>(); public Collection<EmitterClassVisitor> getDefinedClasses() { return definedClasses; } /** * 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>(); public EntryOrderedStore<MethodInfo> getMethodInfos() { return methodInfos; } /** * 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; } public 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; public Traits classTraits; InstanceInfo instanceInfo; public 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."); } }