Skip to content

Commit f85dcae

Browse files
committed
Implemented array literal and array backoff
1 parent 73ea4d6 commit f85dcae

File tree

7 files changed

+105
-18
lines changed

7 files changed

+105
-18
lines changed

.rusty-hook.toml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[hooks]
2+
pre-commit = "cargo clippy && cargo test"
3+
4+
[logging]
5+
verbose = true

crates/ast/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ impl<A: Allocator> Expr<A> {
154154
PrimeExpr::Literal(_) => false,
155155
PrimeExpr::Covered(_) => false,
156156
PrimeExpr::Object(_) => false,
157+
PrimeExpr::Array(_) => false,
157158
PrimeExpr::Function(_, _, _, _) => false,
158159
PrimeExpr::This => false,
159160
},
@@ -181,6 +182,7 @@ pub enum PrimeExpr<A: Allocator> {
181182
Variable(SymbolId),
182183
Covered(Vec<Expr<A>, A>),
183184
Object(Vec<(StringId, Expr<A>), A>),
185+
Array(Vec<Expr<A>, A>),
184186
Function(ScopeId, Option<SymbolId>, Params<A>, Vec<Stmt<A>, A>),
185187
// A direct eval call
186188
Eval(Vec<Expr<A>, A>),

crates/compiler/src/expr.rs

+34
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,10 @@ impl<'a, A: Allocator + Clone> Compiler<'a, A> {
695695
self.compile_object_literal(placement, bindings),
696696
self.alloc.clone(),
697697
),
698+
PrimeExpr::Array(bindings) => ExprValue::new_in(
699+
self.compile_array_literal(placement, bindings),
700+
self.alloc.clone(),
701+
),
698702
PrimeExpr::Function(scope, symbol, args, stmts) => {
699703
let id = self.compile_function_decl(*scope, args, stmts);
700704
if let Some(symbol) = symbol {
@@ -874,6 +878,36 @@ impl<'a, A: Allocator + Clone> Compiler<'a, A> {
874878
object.unwrap()
875879
}
876880

881+
fn compile_array_literal(
882+
&mut self,
883+
placement: Option<Register>,
884+
bindings: &'a Vec<Expr<A>, A>,
885+
) -> Register {
886+
let mut array = None;
887+
for (idx, value) in bindings.iter().enumerate() {
888+
let expr = self.compile_expr(None, value).eval(self);
889+
let key = self.compile_literal(None, Literal::Integer(idx.try_into().unwrap()));
890+
if array.is_none() {
891+
let dst = placement.unwrap_or_else(|| self.builder.alloc_temp());
892+
self.builder.push(Instruction::CreateArray { dst: dst.0 });
893+
array = Some(dst);
894+
}
895+
self.builder.free_temp(expr);
896+
self.builder.free_temp(key);
897+
self.builder.push(Instruction::IndexAssign {
898+
obj: array.unwrap().0,
899+
val: expr.0,
900+
key: key.0,
901+
});
902+
}
903+
if array.is_none() {
904+
let dst = placement.unwrap_or_else(|| self.builder.alloc_temp());
905+
self.builder.push(Instruction::CreateObject { dst: dst.0 });
906+
array = Some(dst);
907+
}
908+
array.unwrap()
909+
}
910+
877911
fn compile_new(&mut self, placement: Option<Register>, rhs: &'a Expr<A>) -> Register {
878912
let func = if let Expr::UnaryPostfix(expr, PostfixOperator::Call(args)) = rhs {
879913
let func = self.compile_expr(None, expr).eval(self);

crates/parser/src/prime.rs

+15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl<'a, A: Allocator + Clone> Parser<'a, A> {
4848
Ok(PrimeExpr::Variable(var))
4949
}
5050
t!("{") => self.parse_object(),
51+
t!("[") => self.parse_array(),
5152
TokenKind::Literal(x) => {
5253
self.next()?;
5354
Ok(match x {
@@ -92,6 +93,20 @@ impl<'a, A: Allocator + Clone> Parser<'a, A> {
9293
Ok(PrimeExpr::Object(exprs))
9394
}
9495

96+
pub(crate) fn parse_array(&mut self) -> Result<PrimeExpr<A>> {
97+
expect!(self, "[");
98+
let mut exprs = Vec::new_in(self.alloc.clone());
99+
while self.peek_kind()? != Some(t!("]")) {
100+
let expr = self.parse_single_expr()?;
101+
exprs.push(expr);
102+
if !self.eat(t!(","))? {
103+
break;
104+
}
105+
}
106+
expect!(self,"]" => "expected array to end here, missing a comma?");
107+
Ok(PrimeExpr::Array(exprs))
108+
}
109+
95110
fn parse_function_expression(&mut self) -> Result<PrimeExpr<A>> {
96111
expect!(self, "function");
97112
let symbol = self

crates/vm/src/instructions/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ define_instructions! {
181181
Move{ dst: u8, src: u8},
182182

183183
CreateObject{ dst: u8},
184+
CreateArray{ dst: u8},
184185

185186
IndexAssign{obj: u8,key: u8, val:u8},
186187
Index{dst: u8,obj: u8, key:u8},

crates/vm/src/object/function.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
Gc, Realm, Value,
99
};
1010
use std::{
11-
cell::{RefCell, UnsafeCell},
11+
cell::{Cell, RefCell, UnsafeCell},
1212
fmt,
1313
};
1414

@@ -71,6 +71,7 @@ impl Object {
7171
array: UnsafeCell::new(Vec::new()),
7272
flags,
7373
function: Some(function),
74+
map_backdown: Cell::new(None),
7475
}
7576
}
7677

crates/vm/src/object/mod.rs

+46-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Javascript object implementation.
22
3-
use std::cell::UnsafeCell;
3+
use std::cell::{Cell, UnsafeCell};
44

55
use crate::{
66
gc::{Gc, Trace},
@@ -11,6 +11,8 @@ use common::collections::HashMap;
1111
mod function;
1212
pub use function::*;
1313

14+
const ARRAY_BACKDOWN_PROCENT: f64 = 0.7;
15+
1416
bitflags::bitflags! {
1517
pub struct ObjectFlags: u8{
1618
const ERROR = 0b1;
@@ -26,6 +28,8 @@ pub struct Object {
2628
array: UnsafeCell<Vec<Value>>,
2729
pub flags: ObjectFlags,
2830
pub function: Option<FunctionKind>,
31+
// TODO non null
32+
map_backdown: Cell<Option<usize>>,
2933
}
3034

3135
impl Object {
@@ -35,6 +39,7 @@ impl Object {
3539
prototype,
3640
values: UnsafeCell::new(HashMap::default()),
3741
array: UnsafeCell::new(Vec::new()),
42+
map_backdown: Cell::new(None),
3843
flags,
3944
function: None,
4045
}
@@ -72,16 +77,19 @@ impl Object {
7277
if key.is_int() {
7378
let idx = key.cast_int();
7479
if idx >= 0 {
75-
return match (*self.array.get()).get(idx as usize).copied() {
76-
Some(x) => Ok(x),
77-
None => {
78-
if let Some(proto) = self.prototype {
79-
proto.index(key, realm)
80-
} else {
81-
Ok(Value::undefined())
80+
let idx = idx as usize;
81+
if self.map_backdown.get().map(|x| idx < x).unwrap_or(true) {
82+
return match (*self.array.get()).get(idx).copied() {
83+
Some(x) => Ok(x),
84+
None => {
85+
if let Some(proto) = self.prototype {
86+
proto.index(key, realm)
87+
} else {
88+
Ok(Value::undefined())
89+
}
8290
}
83-
}
84-
};
91+
};
92+
}
8593
}
8694
}
8795
let string = realm.to_string(key)?;
@@ -110,17 +118,38 @@ impl Object {
110118

111119
if key.is_int() {
112120
let idx = key.cast_int();
113-
if idx >= 0 {
121+
if idx >= 0
122+
&& self
123+
.map_backdown
124+
.get()
125+
.map(|x| (idx as usize) < x)
126+
.unwrap_or(true)
127+
{
114128
let idx = idx as usize;
115-
if (*self.array.get()).len() <= idx {
116-
(*self.array.get()).resize(idx + 1, Value::undefined())
129+
let len = (*self.array.get()).len();
130+
if len <= idx {
131+
if (idx - len) as f64 / len as f64 > ARRAY_BACKDOWN_PROCENT {
132+
// Fill up to capacity since that part is already allocated any way.
133+
let capacity = (*self.array.get()).capacity();
134+
(*self.array.get()).resize(capacity, Value::undefined());
135+
self.map_backdown.set(Some(capacity));
136+
if idx < capacity {
137+
(*self.array.get())[idx] = value;
138+
return Ok(());
139+
}
140+
} else {
141+
(*self.array.get()).resize(idx + 1, Value::undefined());
142+
(*self.array.get())[idx] = value;
143+
return Ok(());
144+
}
145+
} else {
146+
(*self.array.get())[idx] = value;
147+
return Ok(());
117148
}
118-
(*self.array.get())[idx] = value
119149
}
120-
} else {
121-
let string = realm.to_string(key)?;
122-
(*self.values.get()).insert(string.to_string(), value);
123150
}
151+
let string = realm.to_string(key)?;
152+
(*self.values.get()).insert(string.to_string(), value);
124153
Ok(())
125154
}
126155

0 commit comments

Comments
 (0)