/**
   * Create a new compiled code object with the given properties.
   *
   * @param nybbles The nybblecodes.
   * @param locals The number of local variables.
   * @param stack The maximum stack depth.
   * @param functionType The type that the code's functions will have.
   * @param primitive Which primitive to invoke, or zero.
   * @param literals A tuple of literals.
   * @param localTypes A tuple of types of local variables.
   * @param outerTypes A tuple of types of outer (captured) variables.
   * @param module The module in which the code, or nil.
   * @param lineNumber The module line number on which this code starts.
   * @return The new compiled code object.
   */
  public static AvailObject create(
      final A_Tuple nybbles,
      final int locals,
      final int stack,
      final A_Type functionType,
      final @Nullable Primitive primitive,
      final A_Tuple literals,
      final A_Tuple localTypes,
      final A_Tuple outerTypes,
      final A_Module module,
      final int lineNumber) {
    if (primitive != null) {
      // Sanity check for primitive blocks.  Use this to hunt incorrectly
      // specified primitive signatures.
      final boolean canHaveCode = primitive.canHaveNybblecodes();
      assert canHaveCode == (nybbles.tupleSize() > 0);
      final A_Type restrictionSignature = primitive.blockTypeRestriction();
      assert restrictionSignature.isSubtypeOf(functionType);
    } else {
      assert nybbles.tupleSize() > 0;
    }

    assert localTypes.tupleSize() == locals;
    final A_Type argCounts = functionType.argsTupleType().sizeRange();
    final int numArgs = argCounts.lowerBound().extractInt();
    assert argCounts.upperBound().extractInt() == numArgs;
    final int literalsSize = literals.tupleSize();
    final int outersSize = outerTypes.tupleSize();

    assert 0 <= numArgs && numArgs <= 0xFFFF;
    assert 0 <= locals && locals <= 0xFFFF;
    final int slotCount = numArgs + locals + stack;
    assert 0 <= slotCount && slotCount <= 0xFFFF;
    assert 0 <= outersSize && outersSize <= 0xFFFF;

    assert module.equalsNil() || module.isInstanceOf(MODULE.o());
    assert lineNumber >= 0;

    final AvailObject code = mutable.create(literalsSize + outersSize + locals);

    final InvocationStatistic statistic = new InvocationStatistic();
    statistic.countdownToReoptimize.set(L2Chunk.countdownForNewCode());
    final AvailObject statisticPojo = RawPojoDescriptor.identityWrap(statistic);

    code.setSlot(NUM_LOCALS, locals);
    code.setSlot(NUM_ARGS, numArgs);
    code.setSlot(FRAME_SLOTS, slotCount);
    code.setSlot(NUM_OUTERS, outersSize);
    code.setSlot(PRIMITIVE, primitive == null ? 0 : primitive.primitiveNumber);
    code.setSlot(NYBBLES, nybbles.makeShared());
    code.setSlot(FUNCTION_TYPE, functionType.makeShared());
    code.setSlot(PROPERTY_ATOM, NilDescriptor.nil());
    code.setSlot(STARTING_CHUNK, L2Chunk.unoptimizedChunk().chunkPojo);
    code.setSlot(INVOCATION_STATISTIC, statisticPojo);

    // Fill in the literals.
    int dest;
    for (dest = 1; dest <= literalsSize; dest++) {
      code.setSlot(LITERAL_AT_, dest, literals.tupleAt(dest).makeShared());
    }
    for (int i = 1; i <= outersSize; i++) {
      code.setSlot(LITERAL_AT_, dest++, outerTypes.tupleAt(i).makeShared());
    }
    for (int i = 1; i <= locals; i++) {
      code.setSlot(LITERAL_AT_, dest++, localTypes.tupleAt(i).makeShared());
    }
    assert dest == literalsSize + outersSize + locals + 1;

    final A_Atom propertyAtom =
        AtomWithPropertiesDescriptor.create(TupleDescriptor.empty(), module);
    propertyAtom.setAtomProperty(lineNumberKeyAtom(), IntegerDescriptor.fromInt(lineNumber));
    code.setSlot(PROPERTY_ATOM, propertyAtom.makeShared());
    final int hash = propertyAtom.hash() ^ -0x3087B215;
    code.setSlot(HASH, hash);
    code.makeShared();

    // Add the newborn raw function to the weak set being used for code
    // coverage tracking.
    activeRawFunctions.add(code);

    return code;
  }