All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.royale.abc.ABCEmitter Maven / Gradle / Ivy

There is a newer version: 0.9.10
Show newest version
/*
 *
 *  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.royale.abc;

import static org.apache.royale.abc.ABCConstants.CONSTANT_Multiname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_MultinameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_MultinameL;
import static org.apache.royale.abc.ABCConstants.CONSTANT_MultinameLA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_Qname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_QnameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQnameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQnameL;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQnameLA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_TypeName;
import static org.apache.royale.abc.ABCConstants.OP_applytype;
import static org.apache.royale.abc.ABCConstants.OP_astype;
import static org.apache.royale.abc.ABCConstants.OP_bkptline;
import static org.apache.royale.abc.ABCConstants.OP_call;
import static org.apache.royale.abc.ABCConstants.OP_callproperty;
import static org.apache.royale.abc.ABCConstants.OP_callproplex;
import static org.apache.royale.abc.ABCConstants.OP_callpropvoid;
import static org.apache.royale.abc.ABCConstants.OP_callstatic;
import static org.apache.royale.abc.ABCConstants.OP_callsuper;
import static org.apache.royale.abc.ABCConstants.OP_callsupervoid;
import static org.apache.royale.abc.ABCConstants.OP_coerce;
import static org.apache.royale.abc.ABCConstants.OP_construct;
import static org.apache.royale.abc.ABCConstants.OP_constructprop;
import static org.apache.royale.abc.ABCConstants.OP_constructsuper;
import static org.apache.royale.abc.ABCConstants.OP_debug;
import static org.apache.royale.abc.ABCConstants.OP_debugfile;
import static org.apache.royale.abc.ABCConstants.OP_debugline;
import static org.apache.royale.abc.ABCConstants.OP_declocal;
import static org.apache.royale.abc.ABCConstants.OP_declocal_i;
import static org.apache.royale.abc.ABCConstants.OP_deleteproperty;
import static org.apache.royale.abc.ABCConstants.OP_dxns;
import static org.apache.royale.abc.ABCConstants.OP_finddef;
import static org.apache.royale.abc.ABCConstants.OP_findproperty;
import static org.apache.royale.abc.ABCConstants.OP_findpropstrict;
import static org.apache.royale.abc.ABCConstants.OP_getdescendants;
import static org.apache.royale.abc.ABCConstants.OP_getglobalslot;
import static org.apache.royale.abc.ABCConstants.OP_getlex;
import static org.apache.royale.abc.ABCConstants.OP_getlocal;
import static org.apache.royale.abc.ABCConstants.OP_getlocal0;
import static org.apache.royale.abc.ABCConstants.OP_getproperty;
import static org.apache.royale.abc.ABCConstants.OP_getscopeobject;
import static org.apache.royale.abc.ABCConstants.OP_getslot;
import static org.apache.royale.abc.ABCConstants.OP_getsuper;
import static org.apache.royale.abc.ABCConstants.OP_hasnext2;
import static org.apache.royale.abc.ABCConstants.OP_inclocal;
import static org.apache.royale.abc.ABCConstants.OP_inclocal_i;
import static org.apache.royale.abc.ABCConstants.OP_initproperty;
import static org.apache.royale.abc.ABCConstants.OP_istype;
import static org.apache.royale.abc.ABCConstants.OP_kill;
import static org.apache.royale.abc.ABCConstants.OP_lookupswitch;
import static org.apache.royale.abc.ABCConstants.OP_newarray;
import static org.apache.royale.abc.ABCConstants.OP_newcatch;
import static org.apache.royale.abc.ABCConstants.OP_newclass;
import static org.apache.royale.abc.ABCConstants.OP_newfunction;
import static org.apache.royale.abc.ABCConstants.OP_newobject;
import static org.apache.royale.abc.ABCConstants.OP_pushbyte;
import static org.apache.royale.abc.ABCConstants.OP_pushdouble;
import static org.apache.royale.abc.ABCConstants.OP_pushint;
import static org.apache.royale.abc.ABCConstants.OP_pushnamespace;
import static org.apache.royale.abc.ABCConstants.OP_pushscope;
import static org.apache.royale.abc.ABCConstants.OP_pushshort;
import static org.apache.royale.abc.ABCConstants.OP_pushstring;
import static org.apache.royale.abc.ABCConstants.OP_pushuint;
import static org.apache.royale.abc.ABCConstants.OP_returnvoid;
import static org.apache.royale.abc.ABCConstants.OP_setglobalslot;
import static org.apache.royale.abc.ABCConstants.OP_setlocal;
import static org.apache.royale.abc.ABCConstants.OP_setproperty;
import static org.apache.royale.abc.ABCConstants.OP_setslot;
import static org.apache.royale.abc.ABCConstants.OP_setsuper;
import static org.apache.royale.abc.ABCConstants.TRAIT_Class;
import static org.apache.royale.abc.ABCConstants.TRAIT_Const;
import static org.apache.royale.abc.ABCConstants.TRAIT_Function;
import static org.apache.royale.abc.ABCConstants.TRAIT_Getter;
import static org.apache.royale.abc.ABCConstants.TRAIT_Method;
import static org.apache.royale.abc.ABCConstants.TRAIT_Setter;
import static org.apache.royale.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.royale.abc.graph.IBasicBlock;
import org.apache.royale.abc.instructionlist.InstructionList;
import org.apache.royale.abc.semantics.ClassInfo;
import org.apache.royale.abc.semantics.ExceptionInfo;
import org.apache.royale.abc.semantics.InstanceInfo;
import org.apache.royale.abc.semantics.Instruction;
import org.apache.royale.abc.semantics.Label;
import org.apache.royale.abc.semantics.Metadata;
import org.apache.royale.abc.semantics.MethodBodyInfo;
import org.apache.royale.abc.semantics.MethodInfo;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.Nsset;
import org.apache.royale.abc.semantics.PooledValue;
import org.apache.royale.abc.semantics.ScriptInfo;
import org.apache.royale.abc.semantics.Trait;
import org.apache.royale.abc.semantics.Traits;
import org.apache.royale.abc.visitors.IABCVisitor;
import org.apache.royale.abc.visitors.IClassVisitor;
import org.apache.royale.abc.visitors.IDiagnosticsVisitor;
import org.apache.royale.abc.visitors.IMetadataVisitor;
import org.apache.royale.abc.visitors.IMethodBodyVisitor;
import org.apache.royale.abc.visitors.IMethodVisitor;
import org.apache.royale.abc.visitors.IScriptVisitor;
import org.apache.royale.abc.visitors.ITraitVisitor;
import org.apache.royale.abc.visitors.ITraitsVisitor;
import org.apache.royale.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 namePool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    public Pool getNamePool()
    {
    	return namePool;
    }
    
    /**
     * String pool, has default zero entry.
     */
    final Pool stringPool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    public Pool getStringPool()
    {
    	return stringPool;
    }
    
    /**
     * int pool, has default zero entry.
     */
    final Pool intPool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    /**
     * uint pool, has default zero entry.
     */
    final Pool uintPool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    /**
     * double pool, has default zero entry.
     */
    final Pool doublePool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    /**
     * namespace pool, has default zero entry.
     */
    final Pool nsPool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    public Pool getNamespacePool()
    {
    	return nsPool;
    }

    /**
     * namespace set pool, has default zero entry.
     */
    final Pool nssetPool = new Pool(Pool.DefaultType.HasDefaultZero);
    
    /**
     * metadata pool, does not have default zero entry.
     */
    final Pool metadataPool = new Pool(Pool.DefaultType.NoDefaultZero);

    /**
     * Visitors of classes defined in this ABC.
     */
    private Collection definedClasses = new ArrayList();
    
    public Collection 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 methodInfos = new EntryOrderedStore();
    
    public EntryOrderedStore 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 methodBodies = new Vector();
    
    /**
     * Scripts defined in this ABC.  There is normally one.
     */
    private final Vector scriptInfos = new Vector();

    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.
     * 

* This lock is used to ensure that vistEnd calls are only made on the same * thread that called {@link #visit(int, int)}. *

* When running with asserts enabled this lock is used to cause an assertion * failure when the currency rules of this class are violated. *

* 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 = 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 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 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 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 writers = new HashMap(); // generate linear code sequences within the basic blocks, // and compute the offset of each block from the method start. Map block_offsets = new HashMap(); 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 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 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 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: *

    *
  • The {@link Name} object for the qualified name of each {@link Trait}. *
  • *
  • The {@link Name} object that refers to the class or interface of each * {@link Trait} with a type annotation.
  • *
  • The {@link Metadata} objects that decorate each {@link Trait}.
  • *
  • The {@link String}, {@link Namespace}, {@link Double}, * {@link Integer}, or {@link Long} objects that specify the * initial value of each {@link Trait}.
  • *
*/ 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. *

* This method is used to ensure that vistEnd calls are only made on the * same thread that called {@link #visit(int, int)}. *

* When running with asserts enabled this method will cause an assertion * failure when the currency rules of this class are violated. *

* 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."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy