@@ -753,7 +753,25 @@ fn hoist_name<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>, name:
753753 }
754754 }
755755 if env_get_own ( & target_env, name) . is_none ( ) {
756- env_set ( mc, & target_env, name, Value :: Undefined ) ?;
756+ // If target is global object, use CreateGlobalVarBinding semantics
757+ // For indirect eval, CreateGlobalVarBinding(..., true) should be used which creates
758+ // a configurable=true property. Detect indirect eval marker on the global env.
759+ if target_env. borrow ( ) . prototype . is_none ( ) {
760+ let deletable = if let Some ( flag) = crate :: core:: object_get_key_value ( & target_env, "__is_indirect_eval" ) {
761+ matches ! ( * flag. borrow( ) , Value :: Boolean ( true ) )
762+ } else {
763+ false
764+ } ;
765+ let key = PropertyKey :: String ( name. to_string ( ) ) ;
766+ let desc_obj = crate :: core:: new_js_object_data ( mc) ;
767+ object_set_key_value ( mc, & desc_obj, "value" , Value :: Undefined ) ?;
768+ object_set_key_value ( mc, & desc_obj, "writable" , Value :: Boolean ( true ) ) ?;
769+ object_set_key_value ( mc, & desc_obj, "enumerable" , Value :: Boolean ( true ) ) ?;
770+ object_set_key_value ( mc, & desc_obj, "configurable" , Value :: Boolean ( deletable) ) ?;
771+ crate :: js_object:: define_property_internal ( mc, & target_env, & key, & desc_obj) ?;
772+ } else {
773+ env_set ( mc, & target_env, name, Value :: Undefined ) ?;
774+ }
757775 }
758776 Ok ( ( ) )
759777}
@@ -889,6 +907,11 @@ fn hoist_declarations<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>
889907 for stmt in statements {
890908 if let StatementKind :: FunctionDeclaration ( name, params, body, is_generator, is_async) = & * stmt. kind {
891909 let mut body_clone = body. clone ( ) ;
910+ log:: trace!(
911+ "hoist_declarations: found function declaration '{}'; exec env proto is_none={}" ,
912+ name,
913+ env. borrow( ) . prototype. is_none( )
914+ ) ;
892915 if * is_generator {
893916 // Create a generator function object (hoisted)
894917 let func_obj = crate :: core:: new_js_object_data ( mc) ;
@@ -959,8 +982,38 @@ fn hoist_declarations<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>
959982 let func = evaluate_function_expression ( mc, env, None , params, & mut body_clone) ?;
960983 if let Value :: Object ( func_obj) = & func {
961984 object_set_key_value ( mc, func_obj, "name" , Value :: String ( utf8_to_utf16 ( name) ) ) ?;
985+ // CreateGlobalFunctionBinding semantics when executing in the global environment
986+ let key = crate :: core:: PropertyKey :: String ( name. clone ( ) ) ;
987+ if env. borrow ( ) . prototype . is_none ( ) {
988+ let existing = crate :: core:: get_own_property ( env, & key) ;
989+ log:: trace!(
990+ "hoist_declarations: creating global function binding for '{}' existing={:?} is_configurable={}" ,
991+ name,
992+ existing,
993+ env. borrow( ) . is_configurable( & key)
994+ ) ;
995+ if existing. is_none ( ) || env. borrow ( ) . is_configurable ( & key) {
996+ let desc_obj = crate :: core:: new_js_object_data ( mc) ;
997+ object_set_key_value ( mc, & desc_obj, "value" , Value :: Object ( * func_obj) ) ?;
998+ object_set_key_value ( mc, & desc_obj, "writable" , Value :: Boolean ( true ) ) ?;
999+ object_set_key_value ( mc, & desc_obj, "enumerable" , Value :: Boolean ( true ) ) ?;
1000+ object_set_key_value ( mc, & desc_obj, "configurable" , Value :: Boolean ( true ) ) ?;
1001+ crate :: js_object:: define_property_internal ( mc, env, & key, & desc_obj) ?;
1002+ } else {
1003+ let desc_obj = crate :: core:: new_js_object_data ( mc) ;
1004+ object_set_key_value ( mc, & desc_obj, "value" , Value :: Object ( * func_obj) ) ?;
1005+ crate :: js_object:: define_property_internal ( mc, env, & key, & desc_obj) ?;
1006+ }
1007+ log:: trace!(
1008+ "hoist_declarations: after create binding is_configurable={}" ,
1009+ env. borrow( ) . is_configurable( & key)
1010+ ) ;
1011+ } else {
1012+ env_set ( mc, env, name, Value :: Object ( * func_obj) ) ?;
1013+ }
1014+ } else {
1015+ env_set ( mc, env, name, func) ?;
9621016 }
963- env_set ( mc, env, name, func) ?;
9641017 }
9651018 }
9661019 }
@@ -5232,11 +5285,31 @@ fn check_global_declarations<'gc>(env: &JSObjectDataPtr<'gc>, statements: &[Stat
52325285 // Check in reverse order as per spec semantics
52335286 for name in fn_names. iter ( ) . rev ( ) {
52345287 let key = crate :: core:: PropertyKey :: String ( name. clone ( ) ) ;
5235- if crate :: core:: get_own_property ( env, & key) . is_some ( ) && !env. borrow ( ) . is_configurable ( & key) {
5236- return Err ( EvalError :: Js ( crate :: raise_type_error!( format!(
5237- "Cannot declare global function '{}'" ,
5238- name
5239- ) ) ) ) ;
5288+ if let Some ( existing_rc) = crate :: core:: get_own_property ( env, & key) {
5289+ // If it's configurable we can replace it freely
5290+ if env. borrow ( ) . is_configurable ( & key) {
5291+ continue ;
5292+ }
5293+
5294+ // If the existing property is an accessor, defining a data value would be incompatible
5295+ let existing_is_accessor = match & * existing_rc. borrow ( ) {
5296+ crate :: core:: Value :: Property { getter, setter, .. } => getter. is_some ( ) || setter. is_some ( ) ,
5297+ crate :: core:: Value :: Getter ( ..) | crate :: core:: Value :: Setter ( ..) => true ,
5298+ _ => false ,
5299+ } ;
5300+
5301+ if existing_is_accessor {
5302+ return Err ( EvalError :: Js ( crate :: raise_type_error!( format!(
5303+ "Cannot declare global function '{name}'"
5304+ ) ) ) ) ;
5305+ }
5306+
5307+ // If it's a non-writable data property and non-configurable, we cannot change its value
5308+ if !env. borrow ( ) . is_writable ( & key) {
5309+ return Err ( EvalError :: Js ( crate :: raise_type_error!( format!(
5310+ "Cannot declare global function '{name}'"
5311+ ) ) ) ) ;
5312+ }
52405313 }
52415314 }
52425315 Ok ( ( ) )
@@ -7521,13 +7594,43 @@ pub fn evaluate_expr<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>,
75217594 }
75227595}
75237596
7524- fn evaluate_var < ' gc > ( _mc : & MutationContext < ' gc > , env : & JSObjectDataPtr < ' gc > , name : & str ) -> Result < Value < ' gc > , JSError > {
7597+ fn evaluate_var < ' gc > ( mc : & MutationContext < ' gc > , env : & JSObjectDataPtr < ' gc > , name : & str ) -> Result < Value < ' gc > , JSError > {
7598+ // env_get returns the raw stored slot. If a property descriptor (Value::Property) was installed
7599+ // via DefinePropertyOrThrow, unwrap the stored value. For accessor descriptors, call accessor.
75257600 if let Some ( val_ptr) = env_get ( env, name) {
75267601 let val = val_ptr. borrow ( ) . clone ( ) ;
75277602 if let Value :: Uninitialized = val {
75287603 return Err ( raise_reference_error ! ( format!( "Cannot access '{}' before initialization" , name) ) ) ;
75297604 }
7530- return Ok ( val) ;
7605+ match val {
7606+ Value :: Property {
7607+ value : Some ( v) ,
7608+ getter : _,
7609+ setter : _,
7610+ } => return Ok ( v. borrow ( ) . clone ( ) ) ,
7611+ Value :: Property {
7612+ value : None ,
7613+ getter : _,
7614+ setter : _,
7615+ } => return Ok ( Value :: Undefined ) ,
7616+ Value :: Getter ( ..) | Value :: Setter ( ..) => {
7617+ // Unlikely for environment bindings, but support by delegating to accessor call
7618+ // Use helper to invoke accessor and return its result
7619+ if let Some ( val_rc) = env_get ( env, name)
7620+ && let Value :: Getter ( ..) = & * val_rc. borrow ( )
7621+ {
7622+ // Call the getter accessor
7623+ let obj_key = PropertyKey :: String ( name. to_string ( ) ) ;
7624+ let res = get_property_with_accessors ( mc, env, env, & obj_key) . map_err ( |e| match e {
7625+ EvalError :: Js ( js) => js,
7626+ _ => raise_eval_error ! ( "Accessor invocation failed" ) ,
7627+ } ) ?;
7628+ return Ok ( res) ;
7629+ }
7630+ return Err ( raise_eval_error ! ( "Accessor not supported on environment binding" ) ) ;
7631+ }
7632+ other => return Ok ( other) ,
7633+ }
75317634 }
75327635 Err ( raise_reference_error ! ( format!( "{} is not defined" , name) ) )
75337636}
0 commit comments