From 38a3502c8478e66e19c6f85260fc4515ceea1d02 Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Sun, 31 Dec 2023 17:03:59 -0800 Subject: Statically compile simple expressions --- src/ion.rs | 55 +++++++++++++++++-- src/js.rs | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 195 insertions(+), 34 deletions(-) diff --git a/src/ion.rs b/src/ion.rs index 65f5d23..26b54b9 100644 --- a/src/ion.rs +++ b/src/ion.rs @@ -81,7 +81,6 @@ impl IonValue { } pub fn len(&self) -> usize { - // TODO: Should this read the type length? self.0.len() } @@ -116,15 +115,65 @@ impl IonValue { self.reader().next_symbol_id() } + pub fn get_field(&self, field_id: usize) -> Option { + let mut reader = self.reader(); + reader.step_in(); + while reader.offset() < self.len() { + let value = reader.next_value(); + if Some(field_id) == reader.field_id() { + return Some(value); + } + } + None + } } impl From> for IonValue { fn from(value: Vec) -> Self { - if value.len() > 0 { + if value.is_empty() { + IonValue(vec![0x0F]) + } else { IonValue(value) + } + } +} + +impl From> for IonValue { + fn from(values: Vec<(usize, IonValue)>) -> Self { + let mut buf = Vec::new(); + for (k, v) in values { + push_varuint(&mut buf, k); + buf.extend(v.0); + } + let len = buf.len(); + if len < 14 { + buf.insert(0, 0xD0 | len as u8); } else { - IonValue(vec![0x0F]) + let oldbuf = buf; + buf = Vec::with_capacity(oldbuf.len() + 9); + buf.push(0xDE); + push_varuint(&mut buf, len); + buf.extend(oldbuf); + } + IonValue(buf) + } +} + +impl From> for IonValue { + fn from(values: Vec) -> Self { + let len: usize = values.iter().map(|v| v.len()).sum(); + let lenlen = ((usize::BITS - len.leading_zeros() + 6) / 7) as usize; + let mut buf = Vec::with_capacity(1 + len + lenlen); + if len < 14 { + buf.push(0xB0 | len as u8); + } else { + buf.push(0xBE); + push_varuint(&mut buf, len); } + for v in values { + buf.extend(v.0); + } + IonValue(buf) } } diff --git a/src/js.rs b/src/js.rs index 9249d30..d924969 100644 --- a/src/js.rs +++ b/src/js.rs @@ -384,47 +384,40 @@ impl JsCompiler { } } - fn compile_expression(&mut self, expression: &Expression) { + fn get_static_expression(&mut self, expression: &Expression) -> Option { match expression { - Expression::This => self.push_load(0), - Expression::Identifier(ident) => { - let varid = self.get_or_create_variable(ident.sym()); - self.push_load(varid); - }, - Expression::Literal(lit) => { - let value = self.literal_to_ion(lit); - self.push_push(&value); - }, + Expression::Literal(lit) => Some(self.literal_to_ion(lit)), Expression::ArrayLiteral(alit) => { - // TODO: there's an opportunity here inject static arrays into the bytecode. + let mut list = Vec::with_capacity(alit.as_ref().len()); for exprmaybe in alit.as_ref() { if let Some(expr) = exprmaybe { - self.compile_expression(expr); + if let Some(item) = self.get_static_expression(expr) { + list.push(item) + } else { + return None; + } } else { - self.push_push(&IonValue::new_null()); + list.push(IonValue::new_null()) } } - self.push_push(&alit.as_ref().len().into()); - self.push_newlist(); + Some(IonValue::from(list)) }, Expression::ObjectLiteral(olit) => { - // TODO: there's an opportunity here inject static objects into the bytecode. + let mut fields = Vec::with_capacity(olit.properties().len()); for propdef in olit.properties() { match propdef { - PropertyDefinition::IdentifierReference(ident) => { - let varid = self.get_or_create_variable(ident.sym()); - self.push_load(varid); - let symb = self.to_ion_symbol(ident.sym()); - self.push_push(&symb); - }, + PropertyDefinition::IdentifierReference(ident) => return None, PropertyDefinition::Property(name, expr) => { - self.compile_expression(expr); - match name { - PropertyName::Literal(sym) => { - let symb = self.to_ion_symbol(*sym); - self.push_push(&symb); - }, - PropertyName::Computed(_) => todo!(), + if let Some(value) = self.get_static_expression(expr) { + match name { + PropertyName::Literal(sym) => { + let symb = self.to_symbol_id(*sym); + fields.push((symb, value)); + }, + PropertyName::Computed(_) => return None, + } + } else { + return None; } }, PropertyDefinition::MethodDefinition(_, _) => todo!(), @@ -432,8 +425,127 @@ impl JsCompiler { PropertyDefinition::CoverInitializedName(_, _) => todo!(), } } - self.push_push(&olit.properties().len().into()); - self.push_newstruct(); + Some(IonValue::from(fields)) + }, + Expression::Spread(_) => None, + Expression::Function(_) => None, + Expression::ArrowFunction(_) => None, + Expression::AsyncArrowFunction(_) => None, + Expression::Generator(_) => None, + Expression::AsyncFunction(_) => None, + Expression::AsyncGenerator(_) => None, + Expression::Class(_) => None, + Expression::TemplateLiteral(_) => None, + Expression::PropertyAccess(atype) => { + match atype { + PropertyAccess::Simple(access) => { + if let Some(target) = self.get_static_expression(access.target()) { + let field = match access.field() { + PropertyAccessField::Const(sym) => { + Some(self.to_ion_symbol(*sym)) + }, + PropertyAccessField::Expr(expr) => { + self.get_static_expression(expr) + }, + }; + if let Some(field) = field { + return target.get_field(field.to_symbol_id()); + } + } + None + }, + PropertyAccess::Private(_) => todo!(), + PropertyAccess::Super(_) => todo!(), + } + }, + + Expression::Parenthesized(expr) => self.get_static_expression(expr.expression()), + + // TODO: Add static evaluation to these. + Expression::Assign(assignment) => None, + Expression::Unary(_) => None, + Expression::Update(_) => None, + Expression::Binary(binary) => None, + Expression::BinaryInPrivate(_) => None, + Expression::Conditional(_) => None, + Expression::Call(_) => None, + Expression::SuperCall(_) => None, + Expression::ImportCall(_) => None, + + // These probably don't make sense to evaluate statically. + Expression::TaggedTemplate(_) => None, + Expression::NewTarget => None, + Expression::ImportMeta => None, + Expression::Await(_) => None, + Expression::Yield(_) => None, + + // I don't expect it will make sense to evaluate these statically. + Expression::This => None, + Expression::Identifier(_) => None, + Expression::New(_) => None, + Expression::Optional(_) => None, + + _ => todo!(), + } + + } + + fn compile_expression(&mut self, expression: &Expression) { + match expression { + Expression::This => self.push_load(0), + Expression::Identifier(ident) => { + let varid = self.get_or_create_variable(ident.sym()); + self.push_load(varid); + }, + Expression::Literal(lit) => { + let value = self.literal_to_ion(lit); + self.push_push(&value); + }, + Expression::ArrayLiteral(alit) => { + if let Some(value) = self.get_static_expression(expression) { + self.push_push(&value); + } else { + for exprmaybe in alit.as_ref() { + if let Some(expr) = exprmaybe { + self.compile_expression(expr); + } else { + self.push_push(&IonValue::new_null()); + } + } + self.push_push(&alit.as_ref().len().into()); + self.push_newlist(); + } + }, + Expression::ObjectLiteral(olit) => { + if let Some(value) = self.get_static_expression(expression) { + self.push_push(&value); + } else { + for propdef in olit.properties() { + match propdef { + PropertyDefinition::IdentifierReference(ident) => { + let varid = self.get_or_create_variable(ident.sym()); + self.push_load(varid); + let symb = self.to_ion_symbol(ident.sym()); + self.push_push(&symb); + }, + PropertyDefinition::Property(name, expr) => { + self.compile_expression(expr); + match name { + PropertyName::Literal(sym) => { + let symb = self.to_ion_symbol(*sym); + self.push_push(&symb); + }, + PropertyName::Computed(_) => todo!(), + } + }, + PropertyDefinition::MethodDefinition(_, _) => todo!(), + PropertyDefinition::SpreadObject(_) => todo!(), + PropertyDefinition::CoverInitializedName(_, _) => todo!(), + } + } + self.push_push(&olit.properties().len().into()); + self.push_newstruct(); + } }, Expression::Spread(_) => todo!(), Expression::Function(_) => todo!(), -- cgit v1.2.3