Reflective programming

Last updated

In computer science, reflective programming or reflection is the ability of a process to examine, introspect, and modify its own structure and behavior. [1]

Contents

Historical background

The earliest computers were programmed in their native assembly languages, which were inherently reflective, as these original architectures could be programmed by defining instructions as data and using self-modifying code. As the bulk of programming moved to higher-level compiled languages such as Algol, Cobol, Fortran, Pascal, and C, this reflective ability largely disappeared until new programming languages with reflection built into their type systems appeared.[ citation needed ]

Brian Cantwell Smith's 1982 doctoral dissertation introduced the notion of computational reflection in procedural programming languages and the notion of the meta-circular interpreter as a component of 3-Lisp. [2] [3]

Uses

Reflection helps programmers make generic software libraries to display data, process different formats of data, perform serialization and deserialization of data for communication, or do bundling and unbundling of data for containers or bursts of communication.

Effective use of reflection almost always requires a plan: A design framework, encoding description, object library, a map of a database or entity relations.

Reflection makes a language more suited to network-oriented code. For example, it assists languages such as Java to operate well in networks by enabling libraries for serialization, bundling and varying data formats. Languages without reflection such as C are required to use auxiliary compilers for tasks like Abstract Syntax Notation to produce code for serialization and bundling.

Reflection can be used for observing and modifying program execution at runtime. A reflection-oriented program component can monitor the execution of an enclosure of code and can modify itself according to a desired goal of that enclosure. This is typically accomplished by dynamically assigning program code at runtime.

In object-oriented programming languages such as Java, reflection allows inspection of classes, interfaces, fields and methods at runtime without knowing the names of the interfaces, fields, methods at compile time. It also allows instantiation of new objects and invocation of methods.

Reflection is often used as part of software testing, such as for the runtime creation/instantiation of mock objects.

Reflection is also a key strategy for metaprogramming.

In some object-oriented programming languages such as C# and Java, reflection can be used to bypass member accessibility rules. For C#-properties this can be achieved by writing directly onto the (usually invisible) backing field of a non-public property. It is also possible to find non-public methods of classes and types and manually invoke them. This works for project-internal files as well as external libraries such as .NET's assemblies and Java's archives.

Implementation

A language supporting reflection provides a number of features available at runtime that would otherwise be difficult to accomplish in a lower-level language. Some of these features are the abilities to:

These features can be implemented in different ways. In MOO, reflection forms a natural part of everyday programming idiom. When verbs (methods) are called, various variables such as verb (the name of the verb being called) and this (the object on which the verb is called) are populated to give the context of the call. Security is typically managed by accessing the caller stack programmatically: Since callers() is a list of the methods by which the current verb was eventually called, performing tests on callers()[0] (the command invoked by the original user) allows the verb to protect itself against unauthorised use.

Compiled languages rely on their runtime system to provide information about the source code. A compiled Objective-C executable, for example, records the names of all methods in a block of the executable, providing a table to correspond these with the underlying methods (or selectors for these methods) compiled into the program. In a compiled language that supports runtime creation of functions, such as Common Lisp, the runtime environment must include a compiler or an interpreter.

Reflection can be implemented for languages without built-in reflection by using a program transformation system to define automated source-code changes.

Security considerations

Reflection may allow a user to create unexpected control flow paths through an application, potentially bypassing security measures. This may be exploited by attackers. [4] Historical vulnerabilities in Java caused by unsafe reflection allowed code retrieved from potentially untrusted remote machines to break out of the Java sandbox security mechanism. A large scale study of 120 Java vulnerabilities in 2013 concluded that unsafe reflection is the most common vulnerability in Java, though not the most exploited. [5]

Examples

The following code snippets create an instance foo of class Foo and invoke its method PrintHello. For each programming language, normal and reflection-based call sequences are shown.

Common Lisp

The following is an example in Common Lisp using the Common Lisp Object System:

(defclassfoo()())(defmethodprint-hello((ffoo))(formatT"Hello from ~S~%"f));; Normal, without reflection(let((foo(make-instance'foo)))(print-hellofoo));; With reflection to look up the class named "foo" and the method;; named "print-hello" that specializes on "foo".(let*((foo-class(find-class(read-from-string"foo")))(print-hello-method(find-method(symbol-function(read-from-string"print-hello"))nil(listfoo-class))))(funcall(sb-mop:method-generic-functionprint-hello-method)(make-instancefoo-class)))


C#

The following is an example in C#:

// Without reflectionvarfoo=newFoo();foo.PrintHello();// With reflectionObjectfoo=Activator.CreateInstance("complete.classpath.and.Foo");MethodInfomethod=foo.GetType().GetMethod("PrintHello");method.Invoke(foo,null);

Delphi, Object Pascal

This Delphi and Object Pascal example assumes that a TFoo class has been declared in a unit called Unit1:

usesRTTI,Unit1;procedureWithoutReflection;varFoo:TFoo;beginFoo:=TFoo.Create;tryFoo.Hello;finallyFoo.Free;end;end;procedureWithReflection;varRttiContext:TRttiContext;RttiType:TRttiInstanceType;Foo:TObject;beginRttiType:=RttiContext.FindType('Unit1.TFoo')asTRttiInstanceType;Foo:=RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType,[]).AsObject;tryRttiType.GetMethod('Hello').Invoke(Foo,[]);finallyFoo.Free;end;end;

eC

The following is an example in eC:

// Without reflectionFoofoo{};foo.hello();// With reflectionClassfooClass=eSystem_FindClass(__thisModule,"Foo");Instancefoo=eInstance_New(fooClass);Methodm=eClass_FindMethod(fooClass,"hello",fooClass.module);((void(*)())(void*)m.function)(foo);

Go

The following is an example in Go:

import"reflect"// Without reflectionf:=Foo{}f.Hello()// With reflectionfT:=reflect.TypeOf(Foo{})fV:=reflect.New(fT)m:=fV.MethodByName("Hello")ifm.IsValid(){m.Call(nil)}

Java

The following is an example in Java:

importjava.lang.reflect.Method;// Without reflectionFoofoo=newFoo();foo.hello();// With reflectiontry{Objectfoo=Foo.class.getDeclaredConstructor().newInstance();Methodm=foo.getClass().getDeclaredMethod("hello",newClass<?>[0]);m.invoke(foo);}catch(ReflectiveOperationExceptionignored){}

JavaScript

The following is an example in JavaScript:

// Without reflectionconstfoo=newFoo()foo.hello()// With reflectionconstfoo=Reflect.construct(Foo)consthello=Reflect.get(foo,'hello')Reflect.apply(hello,foo,[])// With evaleval('new Foo().hello()')

Julia

The following is an example in Julia:

julia>structPointx::Intyend# Inspection with reflectionjulia>fieldnames(Point)(:x, :y)julia>fieldtypes(Point)(Int64, Any)julia>p=Point(3,4)# Access with reflectionjulia>getfield(p,:x)3

Objective-C

The following is an example in Objective-C, implying either the OpenStep or Foundation Kit framework is used:

// Foo class.@interfaceFoo : NSObject-(void)hello;@end// Sending "hello" to a Foo instance without reflection.Foo*obj=[[Fooalloc]init];[objhello];// Sending "hello" to a Foo instance with reflection.idobj=[[NSClassFromString(@"Foo")alloc]init];[objperformSelector:@selector(hello)];

Perl

The following is an example in Perl:

# Without reflectionmy$foo=Foo->new;$foo->hello;# orFoo->new->hello;# With reflectionmy$class="Foo"my$constructor="new";my$method="hello";my$f=$class->$constructor;$f->$method;# or$class->$constructor->$method;# with evaleval"new Foo->hello;";

PHP

The following is an example in PHP: [6]

// Without reflection$foo=newFoo();$foo->hello();// With reflection, using Reflections API$reflector=newReflectionClass("Foo");$foo=$reflector->newInstance();$hello=$reflector->getMethod("hello");$hello->invoke($foo);

Python

The following is an example in Python:

# Without reflectionobj=Foo()obj.hello()# With reflectionobj=globals()["Foo"]()getattr(obj,"hello")()# With evaleval("Foo().hello()")

R

The following is an example in R:

# Without reflection, assuming foo() returns an S3-type object that has method "hello"obj<-foo()hello(obj)# With reflectionclass_name<-"foo"generic_having_foo_method<-"hello"obj<-do.call(class_name,list())do.call(generic_having_foo_method,alist(obj))

Ruby

The following is an example in Ruby:

# Without reflectionobj=Foo.newobj.hello# With reflectionobj=Object.const_get("Foo").newobj.send:hello# With evaleval"Foo.new.hello"

Xojo

The following is an example using Xojo:

' Without reflectionDimfooInstanceAsNewFoofooInstance.PrintHello' With reflectionDimclassInfoAsIntrospection.Typeinfo=GetTypeInfo(Foo)Dimconstructors()AsIntrospection.ConstructorInfo=classInfo.GetConstructorsDimfooInstanceAsFoo=constructors(0).InvokeDimmethods()AsIntrospection.MethodInfo=classInfo.GetMethodsForEachmAsIntrospection.MethodInfoInmethodsIfm.Name="PrintHello"Thenm.Invoke(fooInstance)EndIfNext

See also

Related Research Articles

<span class="mw-page-title-main">JavaScript</span> High-level programming language

JavaScript, often abbreviated as JS, is a programming language and core technology of the World Wide Web, alongside HTML and CSS. As of 2024, 98.8% of websites use JavaScript on the client side for webpage behavior, often incorporating third-party libraries. All major web browsers have a dedicated JavaScript engine to execute the code on users' devices.

Common Intermediate Language (CIL), formerly called Microsoft Intermediate Language (MSIL) or Intermediate Language (IL), is the intermediate language binary instruction set defined within the Common Language Infrastructure (CLI) specification. CIL instructions are executed by a CIL-compatible runtime environment such as the Common Language Runtime. Languages which target the CLI compile to CIL. CIL is object-oriented, stack-based bytecode. Runtimes typically just-in-time compile CIL instructions into native code.

Generic programming is a style of computer programming in which algorithms are written in terms of data types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. This approach, pioneered by the ML programming language in 1973, permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplicate code.

In computer science, a dynamic programming language is a class of high-level programming languages, which at runtime execute many common programming behaviours that static programming languages perform during compilation. These behaviors could include an extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system. Although similar behaviors can be emulated in nearly any language, with varying degrees of difficulty, complexity and performance costs, dynamic languages provide direct tools to make use of them. Many of these features were first implemented as native features in the Lisp programming language.

In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.

In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. In some languages, particularly C++, function objects are often called functors.

In computer programming, run-time type information or run-time type identification (RTTI) is a feature of some programming languages that exposes information about an object's data type at runtime. Run-time type information may be available for all types or only to types that explicitly have it. Run-time type information is a specialization of a more general concept called type introspection.

In some programming languages, eval, short for the English evaluate, is a function which evaluates a string as though it were an expression in the language, and returns a result; in others, it executes multiple lines of code as though they had been included instead of the line including the eval. The input to eval is not necessarily a string; it may be structured representation of code, such as an abstract syntax tree, or of special type such as code. The analog for a statement is exec, which executes a string as if it were a statement; in some languages, such as Python, both are present, while in other languages only one of either eval or exec is.

This article compares two programming languages: C# with Java. While the focus of this article is mainly the languages and their features, such a comparison will necessarily also consider some features of platforms and libraries. For a more detailed comparison of the platforms, see Comparison of the Java and .NET platforms.

In computing, late binding or dynamic linkage—though not an identical process to dynamically linking imported code libraries—is a computer programming mechanism in which the method being called upon an object, or the function being called with arguments, is looked up by name at runtime. In other words, a name is associated with a particular operation or object at runtime, rather than during compilation. The name dynamic binding is sometimes used, but is more commonly used to refer to dynamic scope.

In class-based, object-oriented programming, a constructor is a special type of function called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured program

The syntax of Java is the set of rules defining how a Java program is written and interpreted.

In computer programming, an entry point is the place in a program where the execution of a program begins, and where the program has access to command line arguments.

<span class="mw-page-title-main">Factory (object-oriented programming)</span> Object that creates other objects

In object-oriented programming, a factory is an object for creating other objects; formally, it is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be "new". More broadly, a subroutine that returns a "new" object may be referred to as a "factory", as in factory method or factory function. The factory pattern is the basis for a number of related software design patterns.

In the Java programming language, the final keyword is used in several contexts to define an entity that can only be assigned once.

this, self, and Me are keywords used in some computer programming languages to refer to the object, class, or other entity which the currently running code is a part of. The entity referred to thus depends on the execution context. Different programming languages use these keywords in slightly different ways. In languages where a keyword like "this" is mandatory, the keyword is the only way to access data and methods stored in the current object. Where optional, these keywords can disambiguate variables and functions with the same name.

<span class="mw-page-title-main">Oxygene (programming language)</span> Object Pascal-based programming language

Oxygene is a programming language developed by RemObjects Software for Microsoft's Common Language Infrastructure, the Java Platform and Cocoa. Oxygene is based on Delphi's Object Pascal, but also has influences from C#, Eiffel, Java, F# and other languages.

<span class="mw-page-title-main">Vala (programming language)</span> Programming language

Vala is an object-oriented programming language with a self-hosting compiler that generates C code and uses the GObject system.

C# 4.0 is a version of the C# programming language that was released on April 11, 2010. Microsoft released the 4.0 runtime and development environment Visual Studio 2010. The major focus of C# 4.0 is interoperability with partially or fully dynamically typed languages and frameworks, such as the Dynamic Language Runtime and COM.

Objective-C is a high-level general-purpose, object-oriented programming language that adds Smalltalk-style messaging to the C programming language. Originally developed by Brad Cox and Tom Love in the early 1980s, it was selected by NeXT for its NeXTSTEP operating system. Due to Apple macOS’s direct lineage from NeXTSTEP, Objective-C was the standard programming language used, supported, and promoted by Apple for developing macOS and iOS applications until the introduction of the Swift programming language in 2014.

References

Citations

  1. A Tutorial on Behavioral Reflection and its Implementation by Jacques Malenfant et al. (PDF), unknown, archived from the original (PDF) on 21 August 2017, retrieved 23 June 2019
  2. Brian Cantwell Smith, Procedural Reflection in Programming Languages, Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD dissertation, 1982.
  3. Brian C. Smith. Reflection and semantics in a procedural language Archived 2015-12-13 at the Wayback Machine . Technical Report MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Massachusetts, January 1982.
  4. Barros, Paulo; Just, René; Millstein, Suzanne; Vines, Paul; Dietl, Werner; d'Amorim, Marcelo; Ernst, Michael D. (August 2015). Static Analysis of Implicit Control Flow: Resolving Java Reflection and Android Intents (PDF) (Report). University of Washington. UW-CSE-15-08-01. Retrieved October 7, 2021.
  5. Eauvidoum, Ieu; disk noise (October 5, 2021). "Twenty years of Escaping the Java Sandbox". Phrack . Vol. 10, no. 46. Retrieved October 7, 2021.
  6. "PHP: ReflectionClass - Manual". www.php.net.

Sources

Further reading