mirror of https://github.com/boa-dev/boa.git
Browse Source
Currently some debugging stuff in JavaScript land is difficult to impossible, like triggering a GC collect, this is not impossible to do in JavaScript the way I triggered it was by creating a huge amount of object `for (let i = 0; i < 100000; ++i) { ({}) }` but this is cumbersome and not guaranteed to trigger a gc. This PR implements `--debug-object` flag that injects the `$boa` debug object in the context, the object is separated into modules currently `gc`, `function`, `object`. We can now do `$boa.gc.collect()`, which force triggers a GC collect. Or sometimes I wanted a trace (the current solution is great, you can trace stuff like `>>> 1 + 1` but that is also it's limitation), it traces everything, I sometimes have a scenario and just want to trace a single function in that scenario, that's why I added the `$boa.function.trace(func, this, ...args)` It only traces the function. ```js >> $boa.function.trace((a, b) => a + b, undefined, 1, 2) -------------------------Compiled Output: ''-------------------------- Location Count Opcode Operands 000000 0000 DefInitArg 0000: 'a' 000005 0001 DefInitArg 0001: 'b' 000010 0002 RestParameterPop 000011 0003 GetName 0000: 'a' 000016 0004 GetName 0001: 'b' 000021 0005 Add 000022 0006 Return 000023 0007 PushUndefined 000024 0008 Return ... (cut for brevity) ... ``` It also implements `$boa.function.flowgraph(func, options)`: ```js $boa.function.flowgraph(func, 'graphviz') $boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' }) ``` Printing the object pointer: ```js $boa.object.id({}) // '0x566464F33' ``` It currently implements some functionality which we can grow it with our debugging needs since we are not restricted by a spec we can add whatever we want :) I was originally going to implement this in #2723 (but the PR is too big), for shapes having functions like: ```js $boa.shape.type({}) // Shared shape $boa.shape.id({}) // 0x8578FG355 (objects, shape pointer) $boa.shape.flowgraph({}) // printing the shape transition chain, like $boa.function.flowgraph ``` Shapes chains are very hard to debug once they are big... so having this type of debugging capability would make it much easier.pull/2787/head
Haled Odat
2 years ago
14 changed files with 586 additions and 1 deletions
@ -0,0 +1,193 @@ |
|||||||
|
use boa_engine::{ |
||||||
|
builtins::function::Function, |
||||||
|
object::ObjectInitializer, |
||||||
|
vm::flowgraph::{Direction, Graph}, |
||||||
|
Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, |
||||||
|
}; |
||||||
|
use boa_interner::ToInternedString; |
||||||
|
|
||||||
|
use crate::FlowgraphFormat; |
||||||
|
|
||||||
|
fn flowgraph_parse_format_option(value: &JsValue) -> JsResult<FlowgraphFormat> { |
||||||
|
if value.is_undefined() { |
||||||
|
return Ok(FlowgraphFormat::Mermaid); |
||||||
|
} |
||||||
|
|
||||||
|
if let Some(string) = value.as_string() { |
||||||
|
return match string.to_std_string_escaped().to_lowercase().as_str() { |
||||||
|
"mermaid" => Ok(FlowgraphFormat::Mermaid), |
||||||
|
"graphviz" => Ok(FlowgraphFormat::Graphviz), |
||||||
|
format => Err(JsNativeError::typ() |
||||||
|
.with_message(format!("Unknown format type '{format}'")) |
||||||
|
.into()), |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
Err(JsNativeError::typ() |
||||||
|
.with_message("format type must be a string") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn flowgraph_parse_direction_option(value: &JsValue) -> JsResult<Direction> { |
||||||
|
if value.is_undefined() { |
||||||
|
return Ok(Direction::LeftToRight); |
||||||
|
} |
||||||
|
|
||||||
|
if let Some(string) = value.as_string() { |
||||||
|
return match string.to_std_string_escaped().to_lowercase().as_str() { |
||||||
|
"leftright" | "lr" => Ok(Direction::LeftToRight), |
||||||
|
"rightleft" | "rl" => Ok(Direction::RightToLeft), |
||||||
|
"topbottom" | "tb" => Ok(Direction::TopToBottom), |
||||||
|
"bottomtop" | "bt " => Ok(Direction::BottomToTop), |
||||||
|
direction => Err(JsNativeError::typ() |
||||||
|
.with_message(format!("Unknown direction type '{direction}'")) |
||||||
|
.into()), |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
Err(JsNativeError::typ() |
||||||
|
.with_message("direction type must be a string") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Get functions instruction flowgraph
|
||||||
|
fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let Some(value) = args.get(0) else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected function argument") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let Some(object) = value.as_object() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message(format!("expected object, got {}", value.type_of())) |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let mut format = FlowgraphFormat::Mermaid; |
||||||
|
let mut direction = Direction::LeftToRight; |
||||||
|
if let Some(arguments) = args.get(1) { |
||||||
|
if let Some(arguments) = arguments.as_object() { |
||||||
|
format = flowgraph_parse_format_option(&arguments.get("format", context)?)?; |
||||||
|
direction = flowgraph_parse_direction_option(&arguments.get("direction", context)?)?; |
||||||
|
} else if value.is_string() { |
||||||
|
format = flowgraph_parse_format_option(value)?; |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("options argument must be a string or object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let object = object.borrow(); |
||||||
|
|
||||||
|
let Some(function) = object.as_function() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected function object") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let code = match function { |
||||||
|
Function::Ordinary { code, .. } |
||||||
|
| Function::Async { code, .. } |
||||||
|
| Function::Generator { code, .. } |
||||||
|
| Function::AsyncGenerator { code, .. } => code, |
||||||
|
Function::Native { .. } => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("native functions do not have bytecode") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let mut graph = Graph::new(direction); |
||||||
|
code.to_graph(context.interner(), graph.subgraph(String::default())); |
||||||
|
let result = match format { |
||||||
|
FlowgraphFormat::Graphviz => graph.to_graphviz_format(), |
||||||
|
FlowgraphFormat::Mermaid => graph.to_mermaid_format(), |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(JsValue::new(result)) |
||||||
|
} |
||||||
|
|
||||||
|
fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let Some(value) = args.get(0) else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected function argument") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let Some(object) = value.as_object() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message(format!("expected object, got {}", value.type_of())) |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
let object = object.borrow(); |
||||||
|
let Some(function) = object.as_function() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected function object") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
let code = match function { |
||||||
|
Function::Ordinary { code, .. } |
||||||
|
| Function::Async { code, .. } |
||||||
|
| Function::Generator { code, .. } |
||||||
|
| Function::AsyncGenerator { code, .. } => code, |
||||||
|
Function::Native { .. } => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("native functions do not have bytecode") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(code.to_interned_string(context.interner()).into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn set_trace_flag_in_function_object(object: &JsObject, value: bool) -> JsResult<()> { |
||||||
|
let object = object.borrow(); |
||||||
|
let Some(function) = object.as_function() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected function object") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
let code = match function { |
||||||
|
Function::Ordinary { code, .. } |
||||||
|
| Function::Async { code, .. } |
||||||
|
| Function::Generator { code, .. } |
||||||
|
| Function::AsyncGenerator { code, .. } => code, |
||||||
|
Function::Native { .. } => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("native functions do not have bytecode") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
code.set_trace(value); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Trace function.
|
||||||
|
fn trace(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let value = args.get_or_undefined(0); |
||||||
|
let this = args.get_or_undefined(1); |
||||||
|
|
||||||
|
let Some(callable) = value.as_callable() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected callable object") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let arguments = args.get(2..).unwrap_or(&[]); |
||||||
|
|
||||||
|
set_trace_flag_in_function_object(callable, true)?; |
||||||
|
let result = callable.call(this, arguments, context); |
||||||
|
set_trace_flag_in_function_object(callable, false)?; |
||||||
|
|
||||||
|
result |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.function(NativeFunction::from_fn_ptr(flowgraph), "flowgraph", 1) |
||||||
|
.function(NativeFunction::from_fn_ptr(bytecode), "bytecode", 1) |
||||||
|
.function(NativeFunction::from_fn_ptr(trace), "trace", 1) |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
use boa_engine::{object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction}; |
||||||
|
|
||||||
|
/// Trigger garbage collection.
|
||||||
|
fn collect(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
boa_gc::force_collect(); |
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.function(NativeFunction::from_fn_ptr(collect), "collect", 0) |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
// Allow lint so it, doesn't warn about `JsResult<>` unneeded return on functions.
|
||||||
|
#![allow(clippy::unnecessary_wraps)] |
||||||
|
|
||||||
|
use boa_engine::{object::ObjectInitializer, property::Attribute, Context, JsObject}; |
||||||
|
|
||||||
|
mod function; |
||||||
|
mod gc; |
||||||
|
mod object; |
||||||
|
mod optimizer; |
||||||
|
|
||||||
|
fn create_boa_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
let function_module = function::create_object(context); |
||||||
|
let object_module = object::create_object(context); |
||||||
|
let optimizer_module = optimizer::create_object(context); |
||||||
|
let gc_module = gc::create_object(context); |
||||||
|
|
||||||
|
ObjectInitializer::new(context) |
||||||
|
.property( |
||||||
|
"function", |
||||||
|
function_module, |
||||||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
) |
||||||
|
.property( |
||||||
|
"object", |
||||||
|
object_module, |
||||||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
) |
||||||
|
.property( |
||||||
|
"optimizer", |
||||||
|
optimizer_module, |
||||||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
) |
||||||
|
.property( |
||||||
|
"gc", |
||||||
|
gc_module, |
||||||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
) |
||||||
|
.build() |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn init_boa_debug_object(context: &mut Context<'_>) { |
||||||
|
let boa_object = create_boa_object(context); |
||||||
|
context.register_global_property( |
||||||
|
"$boa", |
||||||
|
boa_object, |
||||||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
use boa_engine::{ |
||||||
|
object::ObjectInitializer, Context, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Returns objects pointer in memory.
|
||||||
|
fn id(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let Some(value) = args.get(0) else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("expected object argument") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let Some(object) = value.as_object() else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message(format!("expected object, got {}", value.type_of())) |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
let ptr: *const _ = object.as_ref(); |
||||||
|
Ok(format!("0x{:X}", ptr as usize).into()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.function(NativeFunction::from_fn_ptr(id), "id", 1) |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
use boa_engine::{ |
||||||
|
object::{FunctionObjectBuilder, ObjectInitializer}, |
||||||
|
optimizer::OptimizerOptions, |
||||||
|
property::Attribute, |
||||||
|
Context, JsArgs, JsObject, JsResult, JsValue, NativeFunction, |
||||||
|
}; |
||||||
|
|
||||||
|
fn get_constant_folding( |
||||||
|
_: &JsValue, |
||||||
|
_: &[JsValue], |
||||||
|
context: &mut Context<'_>, |
||||||
|
) -> JsResult<JsValue> { |
||||||
|
Ok(context |
||||||
|
.optimizer_options() |
||||||
|
.contains(OptimizerOptions::CONSTANT_FOLDING) |
||||||
|
.into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn set_constant_folding( |
||||||
|
_: &JsValue, |
||||||
|
args: &[JsValue], |
||||||
|
context: &mut Context<'_>, |
||||||
|
) -> JsResult<JsValue> { |
||||||
|
let value = args.get_or_undefined(0).to_boolean(); |
||||||
|
let mut options = context.optimizer_options(); |
||||||
|
options.set(OptimizerOptions::CONSTANT_FOLDING, value); |
||||||
|
context.set_optimizer_options(options); |
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_statistics(_: &JsValue, _: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
Ok(context |
||||||
|
.optimizer_options() |
||||||
|
.contains(OptimizerOptions::STATISTICS) |
||||||
|
.into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn set_statistics(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let value = args.get_or_undefined(0).to_boolean(); |
||||||
|
let mut options = context.optimizer_options(); |
||||||
|
options.set(OptimizerOptions::STATISTICS, value); |
||||||
|
context.set_optimizer_options(options); |
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
let get_constant_folding = |
||||||
|
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_constant_folding)) |
||||||
|
.name("get constantFolding") |
||||||
|
.length(0) |
||||||
|
.build(); |
||||||
|
let set_constant_folding = |
||||||
|
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_constant_folding)) |
||||||
|
.name("set constantFolding") |
||||||
|
.length(1) |
||||||
|
.build(); |
||||||
|
|
||||||
|
let get_statistics = |
||||||
|
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_statistics)) |
||||||
|
.name("get statistics") |
||||||
|
.length(0) |
||||||
|
.build(); |
||||||
|
let set_statistics = |
||||||
|
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_statistics)) |
||||||
|
.name("set statistics") |
||||||
|
.length(1) |
||||||
|
.build(); |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.accessor( |
||||||
|
"constantFolding", |
||||||
|
Some(get_constant_folding), |
||||||
|
Some(set_constant_folding), |
||||||
|
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, |
||||||
|
) |
||||||
|
.accessor( |
||||||
|
"statistics", |
||||||
|
Some(get_statistics), |
||||||
|
Some(set_statistics), |
||||||
|
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, |
||||||
|
) |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
# Boa Debug Object |
||||||
|
|
||||||
|
The `$boa` object contains useful utilities that can be used to debug JavaScript in JavaScript. |
||||||
|
|
||||||
|
It's injected into the context as global variable with the `--debug-object` command-line flag, |
||||||
|
the object is separated into modules. |
||||||
|
|
||||||
|
## Module `$boa.gc` |
||||||
|
|
||||||
|
This module contains functions that are related the garbage collector. It currently has the `.collect()` method. |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
$boa.gc.collect() |
||||||
|
``` |
||||||
|
|
||||||
|
This force triggers the GC to scan the heap and collect garbage. |
||||||
|
|
||||||
|
## Module `$boa.function` |
||||||
|
|
||||||
|
In this module are untility functions related to execution and debugging function. |
||||||
|
|
||||||
|
### Function `$boa.function.bytecode(func)` |
||||||
|
|
||||||
|
This function returns the compiled bytecode of a function as a string, |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
>> function add(x, y) { |
||||||
|
return x + y |
||||||
|
} |
||||||
|
>> $boa.function.bytecode(add) |
||||||
|
" |
||||||
|
------------------------Compiled Output: 'add'------------------------ |
||||||
|
Location Count Opcode Operands |
||||||
|
|
||||||
|
000000 0000 DefInitArg 0000: 'a' |
||||||
|
000005 0001 DefInitArg 0001: 'b' |
||||||
|
000010 0002 RestParameterPop |
||||||
|
000011 0003 GetName 0000: 'a' |
||||||
|
000016 0004 GetName 0001: 'b' |
||||||
|
000021 0005 Add |
||||||
|
000022 0006 Return |
||||||
|
000023 0007 PushUndefined |
||||||
|
000024 0008 Return |
||||||
|
|
||||||
|
Literals: |
||||||
|
<empty> |
||||||
|
|
||||||
|
Bindings: |
||||||
|
0000: a |
||||||
|
0001: b |
||||||
|
|
||||||
|
Functions: |
||||||
|
<empty> |
||||||
|
" |
||||||
|
>> |
||||||
|
``` |
||||||
|
|
||||||
|
### Function `$boa.function.trace(func, this, ...args)` |
||||||
|
|
||||||
|
It only traces the specified function. If the specified function calls other functions, |
||||||
|
their instructions aren't traced. |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
>> const add = (a, b) => a + b |
||||||
|
>> $boa.function.trace(add, undefined, 1, 2) |
||||||
|
5μs DefInitArg 0000: 'a' 2 |
||||||
|
4μs DefInitArg 0001: 'b' <empty> |
||||||
|
0μs RestParameterPop <empty> |
||||||
|
3μs GetName 0000: 'a' 1 |
||||||
|
1μs GetName 0001: 'b' 2 |
||||||
|
2μs Add 3 |
||||||
|
1μs Return 3 |
||||||
|
3 |
||||||
|
>> |
||||||
|
``` |
||||||
|
|
||||||
|
The `this` value can be changed as well as the arguments that are passed to the function. |
||||||
|
|
||||||
|
## Function `$boa.function.flowgraph(func, options)` |
||||||
|
|
||||||
|
It can be used to get the instruction flowgraph, like the command-line flag. |
||||||
|
This works on the function level, allows getting the flow graph without |
||||||
|
quiting the boa shell and adding the specified flags. |
||||||
|
|
||||||
|
Besides the function it also takes an argument that, can be a string or an object. |
||||||
|
If it is a string it represets the flowgraph format, otherwire if it's an object: |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
// These are the defaults, if not specified. |
||||||
|
{ |
||||||
|
format: 'mermaid' |
||||||
|
direction: 'LeftRight' // or 'LR' shorthand. |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
$boa.function.flowgraph(func, 'graphviz') |
||||||
|
$boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' }) |
||||||
|
``` |
||||||
|
|
||||||
|
## Module `$boa.object` |
||||||
|
|
||||||
|
Contains utility functions for getting internal information about an object. |
||||||
|
|
||||||
|
## Function `$boa.object.id(object)` |
||||||
|
|
||||||
|
This function returns memory address of the given object, as a string. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
let o = { x: 10, y: 20 } |
||||||
|
$boa.object.id(o) // '0x7F5B3251B718' |
||||||
|
|
||||||
|
// Geting the address of the $boa object in memory |
||||||
|
$boa.object.id($boa) // '0x7F5B3251B5D8' |
||||||
|
``` |
||||||
|
|
||||||
|
## Module `$boa.optimizer` |
||||||
|
|
||||||
|
This modules contains getters and setters for enabling and disabling optimizations. |
||||||
|
|
||||||
|
### Getter & Setter `$boa.optimizer.constantFolding` |
||||||
|
|
||||||
|
This is and accessor property on the module, its getter returns `true` if enabled or `false` otherwise. |
||||||
|
Its setter can be used to enable/disable the constant folding optimization. |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
$boa.optimizer.constantFolding = true |
||||||
|
$boa.optimizer.constantFolding // true |
||||||
|
``` |
||||||
|
|
||||||
|
### Getter & Setter `$boa.optimizer.statistics` |
||||||
|
|
||||||
|
This is and accessor property on the module, its getter returns `true` if enabled or `false` otherwise. |
||||||
|
Its setter can be used to enable/disable optimization statistics, which are printed to `stdout`. |
||||||
|
|
||||||
|
```JavaScript |
||||||
|
>> $boa.optimizer.constantFolding = true |
||||||
|
>> $boa.optimizer.statistics = true |
||||||
|
>> 1 + 1 |
||||||
|
Optimizer { |
||||||
|
constant folding: 1 run(s), 2 pass(es) (1 mutating, 1 checking) |
||||||
|
} |
||||||
|
|
||||||
|
2 |
||||||
|
>> |
||||||
|
``` |
Loading…
Reference in new issue