Type Conversion Details
Relevant source files
The following files were used as context for generating this wiki page:The Jurassic engine, being a .NET implementation of ECMAScript, requires a robust system for converting values between the JavaScript type system and the underlying .NET types. This is primarily handled by the static TypeConverter class, which implements the JavaScript type conversion rules as defined by the ECMAScript standard. It is complemented by the TypeUtilities class, which provides additional type-related functionality like the typeof operator and the logic for the + (addition) operator.
This system is fundamental to the engine's operation, ensuring that JavaScript code behaves as expected when interacting with different data types. It handles conversions to primitives (boolean, number, string), objects, and various integer formats. The conversion logic is extensively tested, as seen in TypeConverterTests.cs, to ensure compliance with specification edge cases.
Core Conversion Architecture
The type conversion system is centered around two key static classes: TypeConverter and TypeUtilities.
TypeConverter: Provides a suite of static methods for explicit and implicit type coercion according to ECMAScript rules. It is the workhorse for converting any JavaScript value to a target type likeboolean,number,string, orObjectInstance.TypeUtilities: Contains helper functions that build upon theTypeConverter. This includes implementing thetypeofoperator, checking for specific types (IsNumeric,IsString), and handling operations with complex type-dependent behavior, such as the addition operator.
The diagram below illustrates the relationship between these components and how they interact with JavaScript values and the script engine.
This diagram shows that the ScriptEngine utilizes TypeConverter and TypeUtilities to manage JavaScript values. These utilities, in turn, may use the engine to create new object wrappers.
Sources: Jurassic/Core/TypeConverter.cs, Jurassic/Core/TypeUtilities.cs
The TypeConverter Class
The TypeConverter class is the central piece of the type conversion mechanism. It contains static methods that correspond to the abstract operations defined in the ECMAScript specification (e.g., ToBoolean, ToNumber, ToString, ToObject).
Sources: Jurassic/Core/TypeConverter.cs
Generic Conversion: ConvertTo<T>
The ConvertTo<T> method provides a simplified, high-level interface for converting a JavaScript value to a specific .NET type. It acts as a dispatcher, calling the appropriate specialized conversion method based on the target type T.
Target Type T | Internal Method Called |
|---|---|
bool | ToBoolean(value) |
int | ToInteger(value) |
double | ToNumber(value) |
string | ToString(value) |
ObjectInstance | ToObject(engine, value) |
object | Returns the value as-is |
Sources: Jurassic/Core/TypeConverter.cs:34-53, Unit Tests/Core/TypeConverterTests.cs:15-53
Primitive Conversions
ToPrimitive
The ToPrimitive method is a critical internal operation that converts an object to a non-object (primitive) value. The conversion process can be influenced by a PrimitiveTypeHint.
This flowchart shows the logic for converting an object to a primitive value, prioritizing toString or valueOf based on the provided hint.
Sources: Jurassic/Core/TypeConverter.cs:306-314
ToBoolean
The ToBoolean method converts any JavaScript value to a boolean. The conversion follows specific rules, which are summarized in the table below.
| Input Value/Type | Result |
|---|---|
null | false |
Undefined.Value | false |
false (bool) | false |
true (bool) | true |
+0, -0, NaN | false |
| Any other number | true |
"" (empty string) | false |
| Any other string | true |
Symbol | true |
ObjectInstance | true |
Sources: Jurassic/Core/TypeConverter.cs:70-93, Unit Tests/Core/TypeConverterTests.cs:56-82
ToNumber
The ToNumber method converts any JavaScript value to a double.
This sequence diagram illustrates the steps ToNumber takes to convert different types of values, including the recursive step for objects.
Sources: Jurassic/Core/TypeConverter.cs:99-122, Unit Tests/Core/TypeConverterTests.cs:122-181
ToString
The ToString method converts any JavaScript value to a string. It includes a small cache for the most recent number-to-string conversion to improve performance.
| Input Value/Type | Result |
|---|---|
null / Undefined.Value | "undefined" (or custom default) |
Null.Value | "null" |
bool | "true" or "false" |
number | String representation of the number |
string | The string itself |
Symbol | Throws TypeError |
ObjectInstance | Calls ToPrimitive(value, PrimitiveTypeHint.String) then ToString on the result |
Sources: Jurassic/Core/TypeConverter.cs:134-184, Unit Tests/Core/TypeConverterTests.cs:85-119
Object Conversion: ToObject
The ToObject method converts a value to its corresponding ObjectInstance.
- If the input is already an
ObjectInstance, it is returned directly. nullandundefinedvalues cause aTypeErrorto be thrown.- Primitive values (
bool,number,string,Symbol) are wrapped in their corresponding object wrappers (BooleanInstance,NumberInstance,StringInstance,SymbolInstance) using the script engine's built-in constructors.
Sources: Jurassic/Core/TypeConverter.cs:219-261, Unit Tests/Core/TypeConverterTests.cs:522-543
Integer Conversions
The TypeConverter provides several methods to convert JavaScript values to various .NET integer types. These conversions first coerce the value to a number and then apply truncation and/or wrapping.
| Method | Description | Behavior |
|---|---|---|
ToInteger(object) | Converts to a .NET int. This is a non-standard helper. | Coerces to double, then truncates. Clamps to int.MaxValue / int.MinValue for infinities. NaN becomes 0. |
ToInt32(object) | Converts to a 32-bit signed integer. | Coerces to double, then performs a bitwise conversion equivalent to the ECMAScript ToInt32 abstract operation. |
ToUint32(object) | Converts to a 32-bit unsigned integer. | Calls ToInt32 and casts the result to uint. |
ToInt16(object) | Converts to a 16-bit signed integer. | Calls ToInt32 and casts the result to short. |
ToUint16(object) | Converts to an unsigned 16-bit integer. | Calls ToInt32 and casts the result to ushort. |
ToInt8(object) | Converts to an 8-bit signed integer. | Calls ToInt32 and casts the result to sbyte. |
ToUint8(object) | Converts to an unsigned 8-bit integer. | Calls ToInt32 and casts the result to byte. |
Sources: Jurassic/Core/TypeConverter.cs:328-406, Unit Tests/Core/TypeConverterTests.cs:184-519
The TypeUtilities Class
This class provides higher-level, type-related operations that are frequently needed by the engine.
Sources: Jurassic/Core/TypeUtilities.cs
TypeOf Operator
The TypeOf(object obj) method implements the logic for the JavaScript typeof operator. It returns a string indicating the type of the given value.
| Input Value/Type | Returned String |
|---|---|
null / Undefined.Value | "undefined" |
Null.Value | "object" |
bool | "boolean" |
double, int, uint | "number" |
string, ConcatenatedString | "string" |
FunctionInstance | "function" |
Symbol | "symbol" |
ObjectInstance | "object" |
Sources: Jurassic/Core/TypeUtilities.cs:16-36
Addition Operator: Add
The Add(object left, object right) method implements the + operator, which has dual functionality for numeric addition and string concatenation.
This diagram shows that if either operand becomes a string after primitive conversion, the operation is string concatenation; otherwise, it is numeric addition.
Sources: Jurassic/Core/TypeUtilities.cs:134-150
Usage in Generated Code
The TypeConverter methods are heavily used in the auto-generated stub files (.g.cs) that bridge the gap between the .NET implementation of built-in functions and the JavaScript engine. When a JavaScript function is called, these stubs are responsible for converting the incoming object[] arguments to the specific .NET types expected by the C# implementation.
For example, the global isNaN(value) function's stub ensures its argument is converted to a number before execution:
// From Jurassic/Library/GlobalObject.g.cs
private static object __STUB__IsNaN(ScriptEngine engine, object thisObj, object[] args)
{
switch (args.Length)
{
case 0:
return IsNaN(double.NaN);
default:
return IsNaN(TypeConverter.ToNumber(args[0]));
}
}
Sources: Jurassic/Library/GlobalObject.g.cs:74-83
Similarly, methods on built-in prototypes like String.prototype.charAt(pos) use TypeConverter to process arguments.
// From Jurassic/Library/String/StringInstance.g.cs
private static object __STUB__CharAt(ScriptEngine engine, object thisObj, object[] args)
{
if (thisObj == null || thisObj == Undefined.Value || thisObj == Null.Value)
throw new JavaScriptException(ErrorType.TypeError, "Cannot convert undefined or null to object.");
switch (args.Length)
{
case 0:
return CharAt(TypeConverter.ToString(thisObj), 0);
default:
return CharAt(TypeConverter.ToString(thisObj), TypeConverter.ToInteger(args[0]));
}
}
Sources: Jurassic/Library/String/StringInstance.g.cs:52-64
Summary
The type conversion system in Jurassic, primarily implemented in TypeConverter.cs and TypeUtilities.cs, is a crucial low-level feature that ensures the engine's adherence to ECMAScript's type coercion rules. It provides a comprehensive set of functions for converting between JavaScript's dynamic types and the static types of the .NET framework. This system is foundational for the correct execution of built-in functions, operators, and interactions between the script and the host environment. The extensive unit tests in TypeConverterTests.cs validate the correctness of these complex and often subtle conversion rules.