Class inheritance is a fundamental concept in almost all object-oriented programming languages. It describes the ability to have an object or class 'specialize' another one, inheriting parent data and behavior. The subclass (i.e. the inheriting class) defines a subtype of the type of its superclass (i.e. the parent class it inherits from). Here, we think of the type of the class as the collection of its members and their signatures (i.e. the fields of the class with their types as well as its methods with their parameter and return types).
The type of a subclass can extend the type of its superclass by adding new members. Since the only way to interact with an object is by accessing or calling its members, any operation that can be performed with an object of the superclass can also be performed with an object of the subclass. This leads to the notion of subtype polymorphism: objects that belong to the subtype can be used whenever an object of the supertype is expected. That is, one can think of the objects of the subtype as forming a subset of the objects of the supertype. For example, consider the following code snippet:
class A(val x: Int)
class B(x0: Int, val y: Int) extends A(x0)
def f(a: A): Int = a.x
f(new B(1, 2))
Class B
extends class A
, thus forming a subtype relationship
between the types of the two classes. In Scala, this subtype
relationship is expressed by the notation B <: A
.
Since B
is a subtype of A
, it is OK to call the function f
,
which expects an A
with a B
object instead. In particular, the
access to the field x
of a
in the body of f
can be safely
executed on B
instances because class B
inherits all members of
class A
, including field x
.
A superclass A
is open for extension, i.e., it allows behavior to be
extended without modifying A
's source code by adding and overriding
methods in the subclasses of A
. To understand the semantics of calls
to overridden methods, we have to understand the difference between
static and dynamic types.
The static type of an expression in a program is the type that the compiler infers for that expression at compile-time. On the other hand, the dynamic type of an expression is the actual type of the value obtained when the expression is evaluated at run-time. For instance, consider the following code snippet:
class A(val x: Int) {
def m(): Int = x
}
class B(x0: Int, val y: Int) extends A(x0) {
override def m(): Int = x + y
}
def f(a: A) = a.m()
val a: A = new B(1, 2)
a.m()
The static type of a
in the last line is A
. The compiler infers
this type from the type annotation in the declaration of a
on the
previous line. On the other hand, the dynamic type of a
on the last
line is B
since when a
is evaluated at run-time, it refers to the
B
instance created on the previous line.
The behavior of a call to an overridden method such as m
on the last
line is determined by the dynamic type of the receiver expression of
the method call. The call goes to the most recent implementation of
the method in the subtype hierarchy, starting from the dynamic type of
the receiver. Thus, in the example, the call a.m()
on the last line
goes to B.m
and not A.m
. The last line therefore evaluates to 3
and not 1
. This semantics of method calls is referred to as dynamic
dispatch. Methods that are dynamically dispatched are also called
virtual methods. In Scala, all public and protected methods of
classes are virtual by default whereas private methods are non-virtual.
Note that a receiver expression can have more than one dynamic
type. For instance, if we call the function f
with an A
instance,
the dynamic type of a
in f
for this call will be A
and the call
a.m()
in the body of f
will go to A.m
. On the other hand, if we
call f
with a B
instance, then the dynamic type of a
in f
for
this call will be B
and the call to a.m()
in the body of f
will
go to B.m
.
In the following, we will implement subtype polymorphism and dynamic dispatch by hand, leveraging techniques similar to what compilers for OOP languages do when they compile OOP code to executable machine code or byte code.
The goal of this exercise is two-fold:
-
You will obtain a better understanding of what happens when an OOP program is executed and what is the performance overhead associated with using certain OOP features.
-
You will learn how to simulate OOP techniques in languages that do not support object-oriented programming directly.
In our exercise, we will manually translate several classes from the Scala class hierarchy into another programming language in such a way that the obtained program does not rely on OOP features, yet behaves the same way as the original program. We will use C++ as the target of our translation. Of course, C++ is a full-fledged object-oriented language in its own right. So in our exercise we will restrict ourselves to a subset of C++ that does not use OOP features such as class inheritance explicitly. Essentially, we will be using the C subset of C++. We use C++ instead of C out of convenience. In particular, we can use C++ namespaces to simulate the package structure of a Scala program without too much effort.
Before we go into the details of the translation, we first have to discuss two important concepts:
-
Object data layout in memory
-
Virtual method tables (aka vtables)
To implement virtual method dispatch efficiently we need to think
about the data layout of objects in memory. Towards that end, we will
first understand inheritance and virtual methods by looking at the
data layout of objects and vtables. Then we will review C++ code that
implements scala.AnyRef
, scala.String
and java.lang.Class
so
that you can see what these classes look like when they are compiled
to low-level machine code or byte code to be executed in the JVM.
In the following, we will answer these questions:
-
For any given object, how is the data organized in memory?
-
How does the run-time find a particular data member of an instance?
-
How do these things work when dealing with inheritance hierarchies?
-
In particular, how is dynamic dispatch realized?
To get started, consider the following simple Scala classes:
class A(val x: Int, val y: Int)
class B(x1: Int, y1: Int, val z: Int) extends A(x1, y1)
The data members of a class are laid out contiguously in memory for each instance:
A Instance:
0┌─────────────┐
│ value of x │
4├─────────────┤> members of A
│ value of y │
└─────────────┘
B Instance:
0┌─────────────┐
│ value of x │
4├─────────────┤> members of A
│ value of y │
8├═════════════┤
│ value of z │ additional members of B
└─────────────┘
Note that:
-
Each data member can be accessed via a fixed offset from the base address of the data layout. The offset is determined by the number of bytes needed to represent a value of the type of that data member (e.g. 4 bytes for
Int
values and 8 bytes for any type derived fromAnyRef
assuming a 64-bit architecture). -
Subclass objects have the same memory layout as superclass objects with additional space for the subclass members that succeeds the space for the superclass members.
-
Objects of type
B
can be polymorphically operated on as if they were objects of typeA
, since the offsets of the subclass members are the same. E.g. the expressiona.y
wherea
has static typeA
would translate to:-
Take the address stored in the reference
a
, which points to the base of an object data layout. -
Add to it the offset of field
y
in the data layout ofA
, which is 4. -
Dereference the resulting address to retrieve the value of field
y
in the object referred to bya
.
These steps also work if the dynamic type of
a
isB
since the relative offset of the entry fory
in theB
data layout is the same as in theA
data layout. -
Question: If we have polymorphic data structures of variable sizes, how should we pass the data? Answer: by reference. Hence in Scala (and Java), all objects are passed by reference.
Note that because the data layouts of subclass instances are compatible with the data layout of superclass instances, there is no need for the run-time to check the dynamic type of instances in order to access data members.
Now let's add some methods to A
and B
:
class A(val x: Int, val y: Int) {
def m1() = { ... }
def m2() = { ... }
}
class B(x1: Int, y1: Int, val z: Int) extends A(x1, y1) {
override def m2() = { /* overriding A.m2 */ ... }
def m3() = { ... }
}
So should we take the same approach for methods as for data? That is, for each method declared in a class, we could add an entry to the data layout that stores a pointer to the implementation of that method. When we override a method in a subclass, we simply change the pointer at the appropriate entry in the subclass data layout to point to the new implementation. This would give us the following memory representation of an A and a B instance at run-time.
A Instance:
0┌─────────────┐
│ value of x │
4├─────────────┤
│ value of y │
8├─────────────┤ ┌──────────────┐
│ ptr. to m1 │───────────────────>│impl. of A.m1 │
16├─────────────┤ └──────────────┘
│ ptr. to m2 │────────┐ ┌──────────────┐ ^
└─────────────┘ └─>│impl. of A.m2 │ │
└──────────────┘ │
B Instance: │
0┌─────────────┐ │
│ value of x │ │
4├─────────────┤ │
│ value of y │ │
8├─────────────┤ │
│ ptr. to m1 │────────────────────────────────┘
16├─────────────┤ ┌─────────────┐
│ ptr. to m2 │──────────>│impl. of B.m2│
32├═════════════┤ └─────────────┘
│ value of z │
36├─────────────┤ ┌─────────────┐
│ ptr. to m3 │──────────>│impl. of B.m3│
└─────────────┘ └─────────────┘
A call a.m1()
would compile to
-
Take the base address of the data layout pointed to by
a
. -
Add to the base address the relative offset of the pointer to
m1
in the data layout forA
, which is 8. -
Dereference to the resulting address to retrieve the pointer to the correct implementation of
m1
. -
Call the method found at the retrieved address.
Again, this compiled code would work polymorphically for both A
and
B
instances and implement the dynamic dispatch correctly.
However, if we used this approach, then for every method added in a subclass the size of each instance would grow by the size of one pointer. Consequently
-
object creation would be slower, and
-
memory consumption would be higher.
We can avoid wasting space for each method in each object instance by adding an extra level of indirection. When a class defines a virtual method, the compiler adds a hidden member variable to the data layout of that class. This hidden member variable is called the virtual pointer (aka vpointer).
The vpointer points to the virtual method table (aka vtable). Each class has its own vtable which is shared by all instances of that class. The vtable is an array of pointers to functions that implement the virtual methods of the class.
As with the object data layout, the vtable of a subclass has the same layout as the vtable of its superclass, with additional entries for the subclass' virtual methods appended to it. Thus, the offsets of the pointers to the shared methods are the same in both vtables.
The vtable of a subclass is created by copying the entries from the vtable of the superclass and changing the pointers of overridden methods to point to the new implementations. At instance creation (at runtime) the vpointer of the instance will be set to point to the right vtable of the instance's class.
For our example, we would get the following memory representation at run-time
A Instance: A vtable:
0┌─────────────┐ ┌> 0┌────────────┐ ┌─────────────┐
│ vptr │────────┘ │ ptr. to m1 │───────────────────>│impl. of A.m1│
8├─────────────┤ 8├────────────┤ └─────────────┘
│ value of x │ │ ptr. to m2 │────────┐ ┌─────────────┐ ^
12├─────────────┤ └────────────┘ └─>│impl. of A.m2│ │
│ value of y │ └─────────────┘ │
└─────────────┘ │
│
B Instance: B vtable: │
0┌─────────────┐ ┌> 0┌────────────┐ │
│ vptr │────────┘ │ ptr. to m1 │─────────────────────────────┘
8├─────────────┤ 8├────────────┤ ┌─────────────┐
│ value of x │ │ ptr. to m2 │──────────>│impl. of B.m2│
12├─────────────┤ 16├════════════┤ └─────────────┘
│ value of y │ │ ptr. to m3 │────────┐ ┌─────────────┐
16├═════════════┤ └────────────┘ └─>│impl. of B.m3│
│ value of z │ └─────────────┘
└─────────────┘
Note that at run-time, the vpointer of all A
instances will point to
the same vtable data structure, and similarly for B
.
When a call to a virtual method is executed on an instance, the runtime looks up the vtable of the instance's dynamic type via the vpointer, and then looks up the method's implementation for that type via the corresponding pointer in the vtable. The two pointer lookups realize the dynamic dispatch of the method call.
Virtual methods thus add some runtime overhead:
-
The data layout of each object grows by the size of one pointer (to store the vpointer).
-
Each virtual method call involves a constant overhead of two pointer lookups compared to a regular function call (first, to retrieve the address of the right vtable, second to retrieve the address of the correct implementation to which the call should be dispatched).
-
Due to the additional indirection of calling methods via pointers, the compiler also has fewer opportunities for applying static code optimizations such a inlining function calls. Though this is mitigated by just-in-time optimization techniques in modern JVM implementations.
For our manual translation of Scala inheritance to C++ without inheritance, we follow these guidelines:
-
For virtual methods we have a per class vtable containing pointers to the method implementations for that class.
-
Every object has a vpointer pointing to the one vtable of its class.
-
New methods added by a subclass extend the vtable, adding new pointers at the end.
-
Overriden methods go into an existing slot.
-
E.g.
AnyRef
andString
have their own vtables, andString
's vtable is an overridden clone ofAnyRef
's vtable. -
When we translate a Scala class that extends a superclass, we clone the superclass' vtable and add slots for any new methods that are not private to the vtable.
-
Private methods don't go into the vtable because they are not virtual (i.e., can't be overridden and are therefore not dynamically dispatched).
We start by declaring types for our Scala classes with forward
declarations. typedef
declarations are used to define the Scala type
names as pointers to struct
s representing data layouts. In this way
the semantics of the object types are the same as in Scala.
// Forward declarations of data layout and vtables.
struct __Any;
struct __Any_VT;
struct __AnyRef;
struct __AnyRef_VT;
struct __String;
struct __String_VT;
struct __Class;
struct __Class_VT;
// Definition of type names, which are equivalent to Scala semantics,
// i.e., an instance is the address of the object's data layout.
typedef __Any* Any;
typedef __AnyRef* AnyRef;
typedef __Class* Class;
typedef __String* String;
Note that a struct
in C++ is just like a C++ class
except that the
default visibility of members is public
rather than private
.
In our implementation we will ignore memory management. Scala provides automatic memory management via garbage collection whereas C++ uses semi-automatic memory management via destructors. Without accounting for this difference in the semantics of the two languages, the programs resulting from our translation scheme will leak memory. That's OK for this exercise. A more faithful translation would either have to implement a garbage collector, or use C++-style automatic memory management based on smart pointers.
-
Data layout of
scala.AnyRef
in C++struct __AnyRef { // The vpointer __AnyRef_VT* __vptr; // The constructor. __AnyRef(); // The methods implemented by scala.AnyRef. static int32_t hashCode(AnyRef); static bool equals(AnyRef, Any); static Class getClass(AnyRef); static String toString(AnyRef); // The function returning the class object representing // scala.AnyRef. static Class __class(); // The vtable for scala.AnyRef. static __AnyRef_VT __vtable; };
-
__AnyRef
has- a
__vptr
field which is the vpointer to the virtual method table, - a static
__vtable
field which is the one vtable for allAnyRef
instances, - static methods
hashCode
,equals
,getClass
, andtoString
for the implementations of the inbuilt methods ofAnyRef
- and a
__class
method which returns the class object representing the type ofAnyRef
.
- a
-
Note that the static methods such as
equals
that implement the built-in methods of typeAnyRef
all take anAnyRef
as first argument. This first argument will be a pointer to the object on which the method is called (i.e., the implicitthis
parameter of an instance method in Scala). -
Further note that
Int
in Scala has 32 bits, but in C++ the size of typeint
depends on the architecture. So we specifyint32_t
forhashCode
's return type. -
We make
__class
a static method as opposed to a static field to avoid the possibility that it is initialized after other static variables that depend on it during initialization. -
Vtable layout of
scala.AnyRef
:struct __AnyRef_VT { Class __is_a; int32_t (*hashCode)(AnyRef); bool (*equals)(AnyRef, Any); Class (*getClass)(AnyRef); String (*toString)(AnyRef); __Any_VT() : __is_a(__AnyRef::__class()), hashCode(&__AnyRef::hashCode), equals(&__AnyRef::equals), getClass(&__AnyRef::getClass), toString(&__AnyRef::toString) {} };
-
__AnyRef_VT
has- an
__is_a
property which points to theClass
object forAnyRef
, - and function pointers to the methods of
scala.AnyRef
.
- an
-
For example,
int32_t (*hashCode)(AnyRef);
declares a pointer fieldhashCode
to a function that takes anAnyRef
as argument and returns anint32_t
. -
The "no-argument" constructor
__AnyRef_VT()
stores the addresses of__AnyRef
'shashCode
,equals
,getClass
andtoString
implementations in the appropriate fields of the vtable.- Notice how we use
&
to get the address of the functions.
- Notice how we use
-
The data layout of
scala.String
in C++struct __String { // The vpointer (just like in __AnyRef) __String_VT* __vptr; // The field holding the actual data for representing this Scala string std::string data; // The constructor; __String(std::string data); // The methods implemented by scala.String. static int32_t hashCode(String); static bool equals(String, Any); static String toString(String); static int32_t length(String); static char charAt(String, int32_t); // The function returning the class object representing // scala.String. static Class __class(); // The vtable for scala.String. static __String_VT __vtable; };
-
__String
is a clone of__AnyRef
except that it adds a fielddata
to represent the actual string (we use the C++std::string
type for this purpose), and also provides implementations for alength
and acharAt
method. -
Important: the fields that
String
inherits from its superclassAnyRef
are declared first in__String
and they appear in the same order as they appear in the superclasses data layout. New fields are declared after the inherited ones. -
The static methods
hashCode
,equals
, andtoString
are redeclared, but now withString
as the first argument for the implicitthis
parameter. These methods are the implementations that will override the corresponding method implementations ofAnyRef
inString
's vtable. -
There is no new implementation for
getClass
sinceString
does not override thegetClass
method ofAnyRef
. -
The vtable for
scala.String
looks like this// The vtable layout for scala.String. struct __String_VT { Class __is_a; int32_t (*hashCode)(String); bool (*equals)(String, Any); Class (*getClass)(String); String (*toString)(String); int32_t (*length)(String); char (*charAt)(String, int32_t); __String_VT() : __is_a(__String::__class()), hashCode(&__String::hashCode), equals(&__String::equals), getClass( (Class(*)(String)) &__AnyRef::getClass), toString(&__String::toString), length(&__String::length), charAt(&__String::charAt) { } };
-
Again, similar to
__AnyRef_VT
with additional slots for the new methods. -
Important: the pointer for the methods that
String
inherits from its superclassAnyRef
are declared first in__String_VT
and they appear in the same order as they appear in__AnyRef_VT
. The pointers for the new methodslength
andcharAt
are declared after the inherited ones. -
Each vtable entry is initialized with function pointers pointing to the most recent version of the corresponding method, e.g., in
__String_VT
-
getClass
is initialized to point to__AnyRef::getClass
sinceString
does not overridegetClass
. -
all other methods are overriden in
String
or newly added inString
, so we use the new implementations of these methods.
-
-
The type of the first parameter for
__Any
’sgetClass
and__String_V
’sgetClass
differ (the implicitthis
), so we need a cast to make the C++ compiler happy (since we don't use C++ inheritance to express that__String
is a subtype of__AnyRef
, the compiler does not know that aString
can be interpreted as anAnyRef
).
-
We also need to define
java.lang.Class
because every object needs a class object, which is static and shared by all instances of the class. -
The
Class
objects are used to keep track of the dynamic types of objects at run-time. One would need this information to implement Scala's reflection mechanisms such as dynamic type checks for up-casts. We also use theClass
objects to implement the generic default implementation of thetoString
method ofAnyRef
. -
Here is the data layout of
Class
.struct __Class { __Class_VT* __vptr; String name; Class parent; // The constructor. __Class(String name, Class parent); // The instance methods of java.lang.Class. static String toString(Class); static String getName(Class); static Class getSuperclass(Class); static bool isInstance(Class, AnyRef); // The function returning the class object representing // java.lang.Class. static Class __class(); // The vtable for java.lang.Class. static __Class_VT __vtable; };
-
__Class
has aname
field to denote the name of the class as well as aparent
field to reference the parent class. The latter is used to implement thegetSuperclass
method, which returns a reference to an object's superclass. -
The vtable layout for
java.lang.Class
struct __Class_VT { Class __is_a; int32_t (*hashCode)(Class); bool (*equals)(Class, Any); Class (*getClass)(Class); String (*toString)(Class); String (*getName)(Class); Class (*getSuperclass)(Class); bool (*isInstance)(Class, AnyRef); __Class_VT() : __is_a(__Class::__class()), hashCode((int32_t(*)(Class))&__AnyRef::hashCode), equals((bool(*)(Class,Any))&__AnyRef::equals), getClass((Class(*)(Class))&__AnyRef::getClass), toString(&__Class::toString), getName(&__Class::getName), getSuperclass(&__Class::getSuperclass), isInstance(&__Class::isInstance) {} };
-
We need some auxiliary global functions
-
a function that returns the canonical
null
value -
a function to convert a C string literal to a
scala::String
(This is needed so that C++ does not implicitly convert C strings tostd::string
)
-
scala::AnyRef null();
inline scala::String literal(const char * s) {
// C++ implicitly converts the C string to a std::string.
return new scala::__String(s);
}
// scala.AnyRef()
__AnyRef::__AnyRef() : __vptr(&__vtable) {}
// scala.AnyRef.hashCode()
int32_t __AnyRef::hashCode(Any __this) {
return (int32_t)(intptr_t)__this;
}
// scala.AnyRef.equals(Any)
bool __AnyRef::equals(AnyRef __this, Any other) {
return (Any) __this == other;
}
// scala.AnyRef.getClass()
Class __AnyRef::getClass(AnyRef __this) {
return __this->__vptr->__is_a;
}
// scala.AnyRef.toString()
String __AnyRef::toString(AnyRef __this) {
// Class k = this.getClass();
Class k = __this->__vptr->getClass(__this);
std::ostringstream sout;
sout << k->__vptr->getName(k)->data
<< '@' << std::hex << (uintptr_t)__this;
return new __String(sout.str());
}
// Internal accessor for scala.AnyRef's class.
Class __AnyRef::__class() {
static Class k =
new __Class(__rt::literal("java.lang.Object"), (Class) null());
return k;
}
// The vtable for scala.AnyRef. Note that this definition
// invokes the default no-arg constructor for __AnyRef_VT.
__AnyRef_VT __AnyRef::__vtable;
A few notes:
-
hashCode
and the other methods takethis
as an implicit parameter. Sincethis
is a reserved keyword in C++, we use__this
as a reference to the instance receiving the method call. -
For
hashCode
we use the address of the object stored in the__this
parameter as the unique hash code of the instance. However, we cannot cast__this
directly toint32_t
because that would not work on 64-bit architectures. So we cast first tointptr_t
and then toint32_t
. -
See the rest of the code in
scala.cpp
andmain.cpp
-
The implementation of
__Class
is important because without it we would not be able to track the dynamic type of objects (which is needed for reflection).Class
is what links objects in the inheritance hierarchy. -
isInstance
traverses the inheritance hierarchy upwards (until it hitsnull
) to determine whether an object is an instance of a given class.// scala.Class.isInstance(AnyRef) bool __Class::isInstance(Class __this, AnyRef o) { Class k = o->__vptr->getClass(o); do { if (__this->__vptr->equals(__this, (Any) k)) return true; k = k->__vptr->getSuperclass(k); } while ((Class) null() != k); return false; }
-
Variable declarations are translated almost literally. For example, the Scala declaration
var s: String;
will look like this in C++
String s;
Recall that the C++ type
String
is just a short-hand for the type__String*
, i.e., a pointer to the data layout of aString
instance. -
Object creation is also translated almost literally. For example, the Scala statement
s = new String("hello");
is translated to
s = new __String("hello");
Note that we call the constructor
__String
for the data layout of ourString
instances. In C++, thenew
operator returns a pointer to an instance of the type specified by the constructor followingnew
. -
In Scala, the compiler will map string literals such as
"banana"
automatically to values of typeString
. The C++ compiler does not know about the ScalaString
type. So when we translate string literals that appear outside ofString
constructor calls, we have to manually wrap them inString
objects. We can do this with the functionstringLiteral
.For example, the Scala statement
s = "banana";
is translated to
s = stringLiteral("banana");
-
Method calls are translated by looking up the vtable via the vpointer
__vptr
and then calling the appropriate method via the right function pointer. This implements dynamic dispatch. The first argument of the method call is always the receiver of the method call (i.e., the pointer to the instance from which we started the__vptr
lookup).For example, the Scala expression
s.charAt(3)
will be translated to
s->__vptr->charAt(s, 3)
-
When we translate down casts, we have to make the cast explicit because the C++ compiler does not know how the different Scala types relate. For example, the Scala declaration
val a: Any = s;
is translated to
Any a = (Any) s;
For additional examples, see main.cpp
To compile and run the C++ code, you need to install the build tool
cmake
for building C/C++ projects. After installation, open a
terminal in the byhand
directory and execute
cmake .
make
./scala-sim
It is instructive to play with the vtable and data layout declarations
and see how changes affect the execution of the main program. For
example, if we swap the order of the declarations of the function
pointers for hashCode
and length
in __String_VT
like this:
// The vtable layout for scala.String.
struct __String_VT {
Class __is_a;
int32_t (*length)(String); // used to be hashCode
bool (*equals)(String, Any);
Class (*getClass)(String);
String (*toString)(String);
int32_t (*hashCode)(String); // used to be length
char (*charAt)(String, int32_t);
__String_VT()
: __is_a(__String::__class()),
hashCode(&__String::hashCode),
equals(&__String::equals),
getClass( (Class(*)(String)) &__AnyRef::getClass),
toString(&__String::toString),
length(&__String::length),
charAt(&__String::charAt) {
}
};
then the call
a->__vptr->hashCode(a)
on line 18 of main.cpp will go to __String::length
instead of
__String::hash_code
. This is because the offset for the function
pointer lookup in the above call is calculated based on the static
type of a
which is Any
. Thus, the compiler assumes that
a->__vptr
points to a __Any_VT
and in this struct type, the field hashCode
is
the first entry after field __is_a
, whereas in __String_VT
, this entry
now stores the pointer for length
.