@@ -1554,9 +1554,8 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes)
15541554 public static Procedure getProcedure (Target t , Environment env , Script parent , ParseTree ... nodes ) {
15551555 String name = "" ;
15561556 List <IVariable > vars = new ArrayList <>();
1557- ParseTree tree = null ;
15581557 List <String > varNames = new ArrayList <>();
1559- boolean usesAssign = false ;
1558+ boolean procDefinitelyNotConstant = false ;
15601559 CClassType returnType = Auto .TYPE ;
15611560 NodeModifiers modifiers = null ;
15621561 if (nodes [0 ].getData ().equals (CVoid .VOID ) || nodes [0 ].getData ().isInstanceOf (CClassType .TYPE )) {
@@ -1575,71 +1574,120 @@ public static Procedure getProcedure(Target t, Environment env, Script parent, P
15751574 nodes [0 ].getNodeModifiers ().merge (modifiers );
15761575 // We have to restore the variable list once we're done
15771576 IVariableList originalList = env .getEnv (GlobalEnv .class ).GetVarList ().clone ();
1578- for (int i = 0 ; i < nodes .length ; i ++) {
1579- if (i == nodes .length - 1 ) {
1580- tree = nodes [i ];
1581- } else {
1582- boolean thisNodeIsAssign = false ;
1583- if (nodes [i ].getData () instanceof CFunction ) {
1584- String funcName = nodes [i ].getData ().val ();
1585- if (funcName .equals (assign .NAME ) || funcName .equals (__unsafe_assign__ .NAME )) {
1586- thisNodeIsAssign = true ;
1587- if ((nodes [i ].getChildren ().size () == 3 && Construct .IsDynamicHelper (nodes [i ].getChildAt (0 ).getData ()))
1588- || Construct .IsDynamicHelper (nodes [i ].getChildAt (1 ).getData ())) {
1589- usesAssign = true ;
1590- }
1591- } else if (funcName .equals (__autoconcat__ .NAME )) {
1592- throw new CREInvalidProcedureException ("Invalid arguments defined for procedure" , t );
1577+
1578+ // Get code block node.
1579+ ParseTree code = nodes [nodes .length - 1 ];
1580+
1581+ // Execute default parameter values.
1582+ Mixed [] paramDefaultValues = new Mixed [nodes .length - 1 ];
1583+ for (int i = 1 ; i < nodes .length - 1 ; i ++) { // Skip proc name and code block nodes.
1584+ ParseTree node = nodes [i ];
1585+ if (node .getData () instanceof CFunction cf ) {
1586+ if (cf .val ().equals (assign .NAME ) || cf .val ().equals (__unsafe_assign__ .NAME )) {
1587+ ParseTree paramDefaultValueNode = node .getChildAt (node .numberOfChildren () - 1 );
1588+ env .getEnv (GlobalEnv .class ).SetFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED , true );
1589+ try {
1590+ paramDefaultValues [i ] = parent .eval (paramDefaultValueNode , env );
1591+ } finally {
1592+ env .getEnv (GlobalEnv .class ).ClearFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED );
15931593 }
1594+ if (paramDefaultValues [i ] instanceof IVariable ivar ) {
1595+ paramDefaultValues [i ] = env .getEnv (GlobalEnv .class )
1596+ .GetVarList ().get (ivar .getVariableName (), t , true , env ).ival ();
1597+ }
1598+
1599+ // Mark proc as not constant if a default parameter is not a constant.
1600+ if (Construct .IsDynamicHelper (paramDefaultValueNode .getData ())) {
1601+ procDefinitelyNotConstant = true ;
1602+ }
1603+ continue ;
1604+ } else if (cf .val ().equals (__autoconcat__ .NAME )) {
1605+ throw new CREInvalidProcedureException ("Invalid arguments defined for procedure" , t );
15941606 }
1607+ }
1608+ paramDefaultValues [i ] = null ;
1609+ }
1610+
1611+ // Collect parameter IVariable objects with default parameter value assigned.
1612+ for (int i = 0 ; i < nodes .length - 1 ; i ++) {
1613+ IVariable ivar ;
1614+
1615+ // Get IVariable from parameter with type and/or default value.
1616+ Mixed paramDefaultValue = paramDefaultValues [i ];
1617+ if (paramDefaultValue != null ) {
1618+
1619+ // Construct temporary assign node to assign resulting default parameter value.
1620+ ParseTree assignNode = nodes [i ];
1621+ CFunction assignFunc = (CFunction ) assignNode .getData ();
1622+ ParseTree tempAssignNode = new ParseTree (new CFunction (assignFunc .val (),
1623+ assignNode .getTarget ()), assignNode .getFileOptions ());
1624+ if (assignNode .numberOfChildren () == 3 ) {
1625+ tempAssignNode .addChild (assignNode .getChildAt (0 ));
1626+ tempAssignNode .addChild (assignNode .getChildAt (1 ));
1627+ tempAssignNode .addChild (new ParseTree (paramDefaultValue , assignNode .getFileOptions ()));
1628+ } else {
1629+ tempAssignNode .addChild (assignNode .getChildAt (0 ));
1630+ tempAssignNode .addChild (new ParseTree (paramDefaultValue , assignNode .getFileOptions ()));
1631+ }
1632+
1633+ // Assign resulting default parameter value to IVariable.
15951634 env .getEnv (GlobalEnv .class ).SetFlag (GlobalEnv .FLAG_NO_CHECK_DUPLICATE_ASSIGN , true );
15961635 env .getEnv (GlobalEnv .class ).SetFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED , true );
1597- Mixed cons ;
15981636 try {
1599- cons = parent .eval (nodes [ i ] , env );
1637+ ivar = ( IVariable ) parent .eval (tempAssignNode , env );
16001638 } finally {
16011639 env .getEnv (GlobalEnv .class ).ClearFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED );
16021640 env .getEnv (GlobalEnv .class ).ClearFlag (GlobalEnv .FLAG_NO_CHECK_DUPLICATE_ASSIGN );
16031641 }
1642+ } else {
1643+
1644+ // Execute node to obtain proc name or IVariable object.
1645+ env .getEnv (GlobalEnv .class ).SetFlag (GlobalEnv .FLAG_NO_CHECK_DUPLICATE_ASSIGN , true );
1646+ env .getEnv (GlobalEnv .class ).SetFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED , true );
1647+ Mixed val ;
1648+ try {
1649+ val = parent .eval (nodes [i ], env );
1650+ } finally {
1651+ env .getEnv (GlobalEnv .class ).ClearFlag (GlobalEnv .FLAG_VAR_ARGS_ALLOWED );
1652+ env .getEnv (GlobalEnv .class ).ClearFlag (GlobalEnv .FLAG_NO_CHECK_DUPLICATE_ASSIGN );
1653+ }
1654+
1655+ // Handle proc name node.
16041656 if (i == 0 ) {
1605- if (cons instanceof IVariable ) {
1657+ if (val instanceof IVariable ) {
16061658 throw new CREInvalidProcedureException ("Anonymous Procedures are not allowed" , t );
16071659 }
1608- name = cons .val ();
1609- } else {
1610- if (!(cons instanceof IVariable )) {
1611- throw new CREInvalidProcedureException ("You must use IVariables as the arguments" , t );
1612- }
1613- IVariable ivar = null ;
1614- try {
1615- Mixed c = cons ;
1616- String varName = ((IVariable ) c ).getVariableName ();
1617- if (varNames .contains (varName )) {
1618- throw new CREInvalidProcedureException ("Same variable name defined twice in " + name , t );
1619- }
1620- varNames .add (varName );
1621- while (c instanceof IVariable ) {
1622- c = env .getEnv (GlobalEnv .class ).GetVarList ().get (((IVariable ) c ).getVariableName (), t ,
1623- true , env ).ival ();
1624- }
1625- if (!thisNodeIsAssign ) {
1626- //This is required because otherwise a default value that's already in the environment
1627- //would end up getting set to the existing value, thereby leaking in the global env
1628- //into this proc, if the call to the proc didn't have a value in this slot.
1629- c = new CString ("" , t );
1630- }
1631- ivar = new IVariable (((IVariable ) cons ).getDefinedType (),
1632- ((IVariable ) cons ).getVariableName (), c .clone (), t , env );
1633- } catch (CloneNotSupportedException ex ) {
1634- //
1635- }
1636- vars .add (ivar );
1660+ name = val .val ();
1661+ continue ;
16371662 }
1663+
1664+ // Handle proc parameter node.
1665+ if (!(val instanceof IVariable )) {
1666+ throw new CREInvalidProcedureException ("You must use IVariables as the arguments" , t );
1667+ }
1668+ ivar = (IVariable ) val ;
1669+ }
1670+
1671+ // Check for duplicate parameter names.
1672+ String varName = ivar .getVariableName ();
1673+ if (varNames .contains (varName )) {
1674+ throw new CREInvalidProcedureException ("Same variable name defined twice in " + name , t );
16381675 }
1676+ varNames .add (varName );
1677+
1678+ // Get IVariable value.
1679+ Mixed ivarVal = (paramDefaultValue != null ? paramDefaultValue : new CString ("" , t ));
1680+
1681+ // Store IVariable object clone.
1682+ vars .add (new IVariable (ivar .getDefinedType (), ivar .getVariableName (), ivarVal , ivar .getTarget ()));
16391683 }
1684+
1685+ // Restore variable list.
16401686 env .getEnv (GlobalEnv .class ).SetVarList (originalList );
1641- Procedure myProc = new Procedure (name , returnType , vars , nodes [0 ].getNodeModifiers ().getComment (), tree , t );
1642- if (usesAssign ) {
1687+
1688+ // Create and return procedure.
1689+ Procedure myProc = new Procedure (name , returnType , vars , nodes [0 ].getNodeModifiers ().getComment (), code , t );
1690+ if (procDefinitelyNotConstant ) {
16431691 myProc .definitelyNotConstant ();
16441692 }
16451693 return myProc ;
0 commit comments