use crate::internal::as2::hir::{ConstantKind, Expr, ExprKind};
use crate::internal::as2_codegen::builder::CodeBuilder;
use crate::internal::as2_codegen::context::ScriptContext;
use crate::internal::as2_codegen::special_properties::get_special_property;
use crate::internal::as2_codegen::statement::gen_expr;
use crate::internal::as2_pcode::{Action, PushValue};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VariableAccess {
Variable,
Object,
Direct,
SpecialProperty,
}
impl VariableAccess {
pub fn for_identifier(
context: &mut ScriptContext,
builder: &mut CodeBuilder,
name: &str,
) -> Self {
if context.can_use_special_properties()
&& let Some(property) = get_special_property(name)
{
builder.push(context.constants.add(""));
builder.push(property);
return VariableAccess::SpecialProperty;
}
match name {
"null" => {
builder.push(PushValue::Null);
VariableAccess::Direct
}
"undefined" => {
builder.push(PushValue::Undefined);
VariableAccess::Direct
}
name => {
let value = context.constants.add(name);
builder.push(value);
VariableAccess::Variable
}
}
}
pub fn for_constant(
context: &mut ScriptContext,
builder: &mut CodeBuilder,
constant: &ConstantKind,
) -> Self {
match constant {
ConstantKind::String(str) => {
let value = context.constants.add(str);
builder.push(value);
VariableAccess::Direct
}
ConstantKind::Identifier(identifier) => {
Self::for_identifier(context, builder, identifier)
}
ConstantKind::This => Self::for_identifier(context, builder, "this"),
ConstantKind::Float(value) => {
builder.push(*value);
VariableAccess::Direct
}
ConstantKind::Integer(value) => {
builder.push(*value);
VariableAccess::Direct
}
ConstantKind::Boolean(value) => {
builder.push(*value);
VariableAccess::Direct
}
}
}
pub fn for_expr(context: &mut ScriptContext, builder: &mut CodeBuilder, expr: &Expr) -> Self {
match expr {
Expr {
value: ExprKind::Constant(constant),
..
} => Self::for_constant(context, builder, constant),
Expr {
value: ExprKind::Field(object, field),
..
} => {
gen_expr(context, builder, object, false);
gen_expr(context, builder, field, false);
VariableAccess::Object
}
_ => {
gen_expr(context, builder, expr, false);
VariableAccess::Direct
}
}
}
pub fn get_value(&self, builder: &mut CodeBuilder) {
match self {
VariableAccess::Variable => {
builder.action(Action::GetVariable);
}
VariableAccess::Object => {
builder.action(Action::GetMember);
}
VariableAccess::Direct => {}
VariableAccess::SpecialProperty => {
builder.action(Action::GetProperty);
}
}
}
pub fn set_value(&self, builder: &mut CodeBuilder) {
match self {
VariableAccess::Variable => {
builder.action(Action::SetVariable);
}
VariableAccess::Object => {
builder.action(Action::SetMember);
}
VariableAccess::Direct => {
unimplemented!("Cannot set a direct value")
}
VariableAccess::SpecialProperty => {
builder.action(Action::SetProperty);
}
}
}
pub fn get_and_set_value(&self, builder: &mut CodeBuilder) {
match self {
VariableAccess::Variable => {
builder.action(Action::StoreRegister(0));
builder.action(Action::SetVariable);
builder.push(PushValue::Register(0));
}
VariableAccess::Object => {
builder.action(Action::StoreRegister(0));
builder.action(Action::SetMember);
builder.push(PushValue::Register(0));
}
VariableAccess::Direct => {
unimplemented!("Cannot get-and-set a direct value")
}
VariableAccess::SpecialProperty => {
builder.action(Action::StoreRegister(0));
builder.action(Action::SetProperty);
builder.push(PushValue::Register(0));
}
}
}
pub fn delete(&self, builder: &mut CodeBuilder) {
match self {
VariableAccess::Variable => {
builder.action(Action::Delete2);
}
VariableAccess::Object => {
builder.action(Action::Delete);
}
VariableAccess::Direct => {
builder.action(Action::Delete2);
}
VariableAccess::SpecialProperty => {
unimplemented!("Cannot get-and-set a special property")
}
}
}
pub fn call_new(&self, builder: &mut CodeBuilder, num_args: i32) {
match self {
VariableAccess::Variable => {
builder.action_with_stack_delta(Action::NewObject, -num_args - 1);
}
VariableAccess::Object => {
builder.action_with_stack_delta(Action::NewMethod, -num_args - 2);
}
VariableAccess::Direct => {
builder.action_with_stack_delta(Action::NewMethod, -num_args - 2);
}
VariableAccess::SpecialProperty => {
unimplemented!("Cannot call new on a special property")
}
}
}
pub fn call(&self, builder: &mut CodeBuilder, num_args: i32) {
match self {
VariableAccess::Variable => {
builder.action_with_stack_delta(Action::CallFunction, -num_args - 1);
}
VariableAccess::Object => {
builder.action_with_stack_delta(Action::CallMethod, -num_args - 2);
}
VariableAccess::Direct => {
builder.action_with_stack_delta(Action::CallFunction, -num_args - 1);
}
VariableAccess::SpecialProperty => {
unimplemented!("Cannot call a special property")
}
}
}
}