author Eric Bruneton
  import { Edge } from "./Edge" ; import { MethodWriter } from "./MethodWriter" ; import { Opcodes } from "./Opcodes" ; import { ByteVector } from "./ByteVector" ; import { Frame } from "./Frame" ; import { ClassReader } from "./ClassReader" ; import * as bits from "./bits" ; import { assert } from "./utils" ; export class Label { /**
     * Indicates if this label is only used for debug attributes. Such a label
     * is not the start of a basic block, the target of a jump instruction, or
     * an exception handler. It can be safely ignored in control flow graph
     * analysis algorithms (for optimization purposes).
     */ static DEBUG : number = 1 ; /**
     * Indicates if the position of this label is known.
     */ static RESOLVED : number = 2 ; /**
     * Indicates if this label has been updated, after instruction resizing.
     */ static RESIZED : number = 4 ; /**
     * Indicates if this basic block has been pushed in the basic block stack.
     * See  MethodWriter .
      static PUSHED : number = 8 ; /**
     * Indicates if this label is the target of a jump instruction, or the start
     * of an exception handler.
     */ static TARGET : number = 16 ; /**
     * Indicates if a stack map frame must be stored for this label.
     */ static STORE : number = 32 ; /**
     * Indicates if this label corresponds to a reachable basic block.
     */ static REACHABLE : number = 64 ; /**
     * Indicates if this basic block ends with a JSR instruction.
     */ static JSR : number = 128 ; /**
     * Indicates if this basic block ends with a RET instruction.
     */ static RET : number = 256 ; /**
     * Indicates if this basic block is the start of a subroutine.
     */ static SUBROUTINE : number = 512 ; /**
     * Indicates if this subroutine basic block has been visited by a
     * visitSubroutine(null, ...) call.
     */ static VISITED : number = 1024 ; /**
     * Indicates if this subroutine basic block has been visited by a
     * visitSubroutine(!null, ...) call.
     */ static VISITED2 : number = 2048 ; /**
     * Field used to associate user information to a label. Warning: this field
     * is used by the ASM tree package. In order to use it with the ASM tree
     * package you must override the
     *  org . objectweb . asm . tree . MethodNode method.
      public info : any ; see see see see see see see see see status : number ; /**
     * The line number corresponding to this label, if known. If there are
     * several lines, each line is stored in a separate label, all linked via
     * their next field (these links are created in ClassReader and removed just
     * before visitLabel is called, so that this does not impact the rest of the
     * code).
     */ line : number ; /**
     * The position of this label in the code, if known.
     */ position : number ; /**
     * Number of forward references to this label, times two.
     */ private referenceCount : number ; /**
     * Informations about forward references. Each forward reference is
     * described by two consecutive integers in this array: the first one is the
     * position of the first byte of the bytecode instruction that contains the
     * forward reference, while the second is the position of the first byte of
     * the forward reference itself. In fact the sign of the first integer
     * indicates if this reference uses 2 or 4 bytes, and its absolute value
     * gives the position of the bytecode instruction. This array is also used
     * as a bitset to store the subroutines to which a basic block belongs. This
     * information is needed in {@linked MethodWriter#visitMaxs}, after all
     * forward references have been resolved. Hence the same array can be used
     * for both purposes without problems.
     */ private srcAndRefPositions : number [ ] | null = null ; /**
     * Start of the output stack relatively to the input stack. The exact
     * semantics of this field depends on the algorithm that is used.
     *
     * When only the maximum stack size is computed, this field is the number of
     * elements in the input stack.
     *
     * When the stack map frames are completely computed, this field is the
     * offset of the first output stack element relatively to the top of the
     * input stack. This offset is always negative or null. A null offset means
     * that the output stack must be appended to the input stack. A -n offset
     * means that the first n output stack elements must replace the top n input
     * stack elements, and that the other elements must be appended to the input
     * stack.
     */ inputStackTop : number ; /**
     * Maximum height reached by the output stack, relatively to the top of the
     * input stack. This maximum is always positive or null.
     */ outputStackMax : number ; /**
     * Information about the input and output stack map frames of this basic
     * block. This field is only used when  ClassWriter * option is used.
      frame : Frame | null = null ; /**
     * The successor of this label, in the order they are visited. This linked
     * list does not include labels used for debug info only. If
     *  ClassWriter option is used then, in addition, it
     * does not contain successive labels that denote the same bytecode position
     * (in this case only the first label appears in this list).
      successor ! : Label ; /**
     * The successors of this node in the control flow graph. These successors
     * are stored in a linked list of  Edge objects, linked to each
     * other by their  Edge field.
      successors ! : Edge ; /**
     * The next basic block in the basic block stack. This stack is used in the
     * main loop of the fix point algorithm used in the second step of the
     * control flow analysis algorithms. It is also used in
     *  {@link #visitSubroutine} to avoid using a recursive method, and in
     * ClassReader to temporarily store multiple source lines for a label.
     *
     *  see MethodWriter next : Label | null = null ; /**
     * Constructs a new label.
     */ public constructor ( ) { this . status = 0 ; this . line = 0 ; this . position = 0 ; this . referenceCount = 0 ; this . inputStackTop = 0 ; this . outputStackMax = 0 ; } /**
     * Returns the offset corresponding to this label. This offset is computed
     * from the start of the method's bytecode. <i>This method is intended for
     *  Attribute sub classes, and is normally not needed by class
     * generators or adapters.</i>
     *
     *  return throws public getOffset ( ) { if ( ( this . status & Label . RESOLVED ) === 0 ) { throw new Error ( "Label offset position has not been resolved yet" ) ; } return this . position ; } param owner param out param source param wideOffset throws put ( owner , out , source , wideOffset ) { if ( ( this . status & Label . RESOLVED ) === 0 ) { if ( wideOffset ) { this . addReference ( - 1 - source , out . length ) ; out . putInt ( - 1 ) ; } else { this . addReference ( source , out . length ) ; out . putShort ( - 1 ) ; } } else { if ( wideOffset ) { out . putInt ( this . position - source ) ; } else { out . putShort ( this . position - source ) ; } } } param sourcePosition param referencePosition private addReference ( sourcePosition , referencePosition ) { if ( this . srcAndRefPositions == null ) { this . srcAndRefPositions = new Array ( 6 ) ; } if ( this . referenceCount >= this . srcAndRefPositions . length ) { let a = new Array ( this . srcAndRefPositions . length + 6 ) ; a . concat ( this . srcAndRefPositions ) ; this . srcAndRefPositions = a ; } this . srcAndRefPositions [ this . referenceCount ++ ] = sourcePosition ; this . srcAndRefPositions [ this . referenceCount ++ ] = referencePosition ; } param owner param position param data return throws resolve ( owner , position , data ) { assert ( this . srcAndRefPositions ) ; let needUpdate = false ; this . status |= Label . RESOLVED ; this . position = position ; let i = 0 ; while ( ( i < this . referenceCount ) ) { let source = this . srcAndRefPositions [ i ++ ] ; let reference = this . srcAndRefPositions [ i ++ ] ; let offset ; if ( source >= 0 ) { offset = position - source ; if ( offset < bits . SHORT_MIN || offset > bits . SHORT_MAX ) { let opcode = data [ reference - 1 ] & 255 ; if ( opcode <= Opcodes . JSR ) { data [ reference - 1 ] = ( ( opcode + 49 ) | 0 ) ; } else { data [ reference - 1 ] = ( ( opcode + 20 ) | 0 ) ; } needUpdate = true ; } data [ reference ++ ] = ( ( offset >>> 8 ) | 0 ) ; data [ reference ] = ( offset | 0 ) ; } else { offset = position + source + 1 ; data [ reference ++ ] = ( ( offset >>> 24 ) | 0 ) ; data [ reference ++ ] = ( ( offset >>> 16 ) | 0 ) ; data [ reference ++ ] = ( ( offset >>> 8 ) | 0 ) ; data [ reference ] = ( offset | 0 ) ; } } ; return needUpdate ; } return getFirst ( ) { return ! ClassReader . FRAMES || this . frame == null ? this : this . frame . owner ; } param id return inSubroutine ( id ) { assert ( this . srcAndRefPositions ) ; if ( ( this . status & Label . VISITED ) !== 0 ) { return ( this . srcAndRefPositions [ ( ( id >>> 32 ) | 0 ) ] & ( id | 0 ) ) !== 0 ; } return false ; } param block return inSameSubroutine ( block ) { if ( ( this . status & Label . VISITED ) === 0 || ( block . status & Label . VISITED ) === 0 ) { return false ; } assert ( this . srcAndRefPositions ) ; assert ( block . srcAndRefPositions ) ; for ( let i = 0 ; i < this . srcAndRefPositions . length ; ++ i ) { if ( ( this . srcAndRefPositions [ i ] & block . srcAndRefPositions [ i ] ) !== 0 ) { return true ; } } return false ; } param id param nbSubroutines addToSubroutine ( id , nbSubroutines ) { assert ( this . srcAndRefPositions ) ; if ( ( this . status & Label . VISITED ) === 0 ) { this . status |= Label . VISITED ; this . srcAndRefPositions = new Array ( ( nbSubroutines / 32 | 0 ) + 1 ) ; } this . srcAndRefPositions [ ( ( id >>> 32 ) | 0 ) ] |= ( id | 0 ) ; } param JSR param id param nbSubroutines visitSubroutine ( JSR , id , nbSubroutines ) { let stack = this ; while ( ( stack != null ) ) { let l = stack ; stack = l . next ; l . next = null ; if ( JSR != null ) { if ( ( l . status & Label . VISITED2 ) !== 0 ) { continue ; } l . status |= Label . VISITED2 ; if ( ( l . status & Label . RET ) !== 0 ) { if ( ! l . inSameSubroutine ( JSR ) ) { let e = new Edge ( ) ; e . info = l . inputStackTop ; e . successor = JSR . successors . successor ; e . next = l . successors ; l . successors = e ; } } } else { if ( l . inSubroutine ( id ) ) { continue ; } l . addToSubroutine ( id , nbSubroutines ) ; } let e = l . successors ; while ( ( e != null ) ) { if ( ( l . status & Label . JSR ) === 0 || ( l . successors && e !== l . successors . next ) ) { if ( e . successor && e . successor . next == null ) { e . successor . next = stack ; stack = e . successor ; } } e = e . next ; } ; } ; } return public toString ( ) { return "Lable" ; } }