UseStrictInstruction.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Translator */
40 /* */
41 /* Primitive USE STRICT instruction class */
42 /* */
43 /******************************************************************************/
44 #include <stdlib.h>
45 #include "RexxCore.h"
46 #include "ArrayClass.hpp"
47 #include "QueueClass.hpp"
48 #include "RexxActivation.hpp"
49 #include "UseStrictInstruction.hpp"
51 #include <algorithm>
52 
53 RexxInstructionUseStrict::RexxInstructionUseStrict(size_t count, bool strict, bool extraAllowed, bool autoCreate, bool named, RexxQueue *variable_list, RexxQueue *defaults)
54 {
55  if (strict && autoCreate && named && !extraAllowed)
56  {
57  // use strict auto named arg
58  reportException(Error_Translation_user_defined, "STRICT AUTO requires the \"...\" argument marker at the end of the argument list");
59  }
60 
61  // set the variable count and the option flag
62  variableCount = count;
63  variableSize = extraAllowed; // we might allow an unchecked number of additional arguments
64  minimumRequired = 0; // do don't necessarily require any of these.
65  strictChecking = strict; // record if this is the strict form
66  autoCreation = autoCreate;
67  namedArg = named;
68 
69  // items are added to the queues in reverse order, so we pop them off and add
70  // them to the end of the list as we go.
71  while (count > 0) // loop through our variable set, adding everything in.
72  {
73  // decrement first, so we store at the correct offset.
74  count--;
75  OrefSet(this, variables[count].variable, (RexxVariableBase *)variable_list->pop());
76  OrefSet(this, variables[count].defaultValue, defaults->pop());
77 
78  if (this->strictChecking)
79  {
80  if (this->namedArg)
81  {
82  if (variables[count].defaultValue == OREF_NULL) minimumRequired++;
83  }
84  else
85  {
86  // if this is a real variable, see if this is the last of the required ones.
87  if (minimumRequired < count + 1 && variables[count].variable != OREF_NULL)
88  {
89  // no default value means this is a required argument, this is the min we'll accept.
90  if (variables[count].defaultValue == OREF_NULL)
91  {
92  minimumRequired = count + 1;
93  }
94  }
95  }
96  }
97  }
98 
99  if (this->namedArg) this->checkNamedArguments();
100 }
101 
102 
103 /**
104  * The runtime, non-debug live marking routine.
105  */
106 void RexxInstructionUseStrict::live(size_t liveMark)
107 {
108  size_t i; /* loop counter */
109  size_t count; /* argument count */
110 
111  memory_mark(this->nextInstruction); /* must be first one marked */
112  for (i = 0, count = variableCount; i < count; i++)
113  {
114  memory_mark(this->variables[i].variable);
115  memory_mark(this->variables[i].defaultValue);
116  }
117 }
118 
119 
120 /**
121  * The generalized live marking routine used for non-performance
122  * critical marking operations.
123  */
125 {
126  size_t i; /* loop counter */
127  size_t count; /* argument count */
128 
129  /* must be first one marked */
131  for (i = 0, count = variableCount; i < count; i++)
132  {
133  memory_mark_general(this->variables[i].variable);
134  memory_mark_general(this->variables[i].defaultValue);
135  }
136 }
137 
138 
139 /**
140  * The flattening routine, used for serializing object trees.
141  *
142  * @param envelope The envelope were's flattening into.
143  */
145 {
146  size_t i; /* loop counter */
147  size_t count; /* argument count */
148 
150 
151  flatten_reference(newThis->nextInstruction, envelope);
152  for (i = 0, count = variableCount; i < count; i++)
153  {
154  flatten_reference(newThis->variables[i].variable, envelope);
155  flatten_reference(newThis->variables[i].defaultValue, envelope);
156  }
158 }
159 
160 
162 {
163  if (this->namedArg) this->executeNamedArguments(context, stack);
164  else this->executePositionalArguments(context, stack);
165 }
166 
167 
169 {
170  context->traceInstruction(this); // trace if necessary
171  // get the argument information from the context
172  RexxObject **arglist = context->getMethodArgumentList();
173  size_t argcount = context->getMethodArgumentCount();
174  // strict checking means we need to enforce min/max limits
175  if (strictChecking)
176  {
177  // not enough of the required arguments? That's an error
178  if (argcount < minimumRequired)
179  {
180  // this is a pain, but there are different errors for method errors vs. call errors.
181  if (context->inMethod())
182  {
184  }
185  else
186  {
188  }
189  }
190  // potentially too many?
191  if (!variableSize && argcount > variableCount)
192  {
193  if (context->inMethod())
194  {
196  }
197  else
198  {
200  }
201  }
202  }
203 
204  // now we process each of the variable definitions left-to-right
205  for (size_t i = 0; i < variableCount; i++)
206  {
207  // get our current variable. We're allowed to skip over variables, so
208  // there might not be anything here.
209  RexxVariableBase *variable = variables[i].variable;
210  if (variable != OREF_NULL)
211  {
212  // get the corresponding argument
213  RexxObject *argument = getArgument(arglist, argcount, i);
214  if (argument != OREF_NULL)
215  {
216  context->traceResult(argument); // trace if necessary
217  // assign the value
218  variable->assign(context, stack, argument);
219  }
220  else
221  {
222  // grab a potential default value
223  RexxObject *defaultValue = variables[i].defaultValue;
224 
225  // and omitted argument is only value if we've marked it as optional
226  // by giving it a default value
227  if (defaultValue != OREF_NULL)
228  {
229  // evaluate the default value now
230  defaultValue = defaultValue->evaluate(context, stack);
231  context->traceResult(defaultValue); // trace if necessary
232  // assign the value
233  variable->assign(context, stack, defaultValue);
234  stack->pop(); // remove the value from the stack
235  }
236  else
237  {
238  // not doing strict checks, revert to old rules and drop the variable.
239  if (!strictChecking)
240  {
241  variable->drop(context);
242 
243  }
244  else
245  {
246  if (context->inMethod())
247  {
248  reportException(Error_Incorrect_method_noarg, OREF_positional, i + 1);
249  }
250  else
251  {
252  reportException(Error_Incorrect_call_noarg, context->getCallname(), OREF_positional, i + 1);
253  }
254  }
255  }
256  }
257  }
258  }
259  context->pauseInstruction(); // do debug pause if necessary
260 }
261 
262 
263 /**
264  * Get the argument corresponding to a given argument position.
265  *
266  * @param arglist The argument list for the method.
267  * @param count The argument count.
268  * @param target The target argument offset.
269  *
270  * @return The argument corresponding to the position. Returns OREF_NULL
271  * if the argument doesn't exist.
272  */
273 RexxObject *RexxInstructionUseStrict::getArgument(RexxObject **arglist, size_t count, size_t target)
274 {
275  // is this beyond what we've been provided with?
276  if (target + 1 > count)
277  {
278  return OREF_NULL;
279  }
280  // return the target item
281  return arglist[target];
282 }
283 
284 
286 {
287  context->traceInstruction(this); // trace if necessary
288  // get the argument information from the context
289  RexxObject **arglist = context->getMethodArgumentList();
290  size_t argcount = context->getMethodArgumentCount();
291  size_t named_argcount = context->getMethodNamedArgumentCount();
292 
293  // strict checking means we need to enforce min/max limits
294  if (strictChecking)
295  {
296  // not enough of the required arguments? That's an error
297  if (named_argcount < minimumRequired)
298  {
299  if (context->inMethod())
300  {
302  }
303  else
304  {
306  }
307  }
308  // potentially too many?
309  if (!variableSize && named_argcount > variableCount)
310  {
311  if (context->inMethod())
312  {
314  }
315  else
316  {
318  }
319  }
320  }
321 
322  // Helper storage to associate the values passed by the caller to the expected arguments declared in the USE instruction
323  NamedArguments expectedNamedArguments(this->variableCount);
324 
325  // Iterate over the named arguments declared by the callee with the instruction USE NAMED ARG.
326  // Collect their names.
327  for (size_t i = 0; i < this->variableCount; i++)
328  {
329  // Remember: code at risk! I don't use the constructor, and that could bring troubles
330  // as I had when I added nameLength and forgot to put a value here...
331  RexxVariableBase *variable = this->variables[i].variable;
332  expectedNamedArguments[i].name = variable->getName()->getStringData();
333  expectedNamedArguments[i].nameLength = variable->getName()->getLength();
334  }
335 
336  // Iterate over the named arguments passed by the caller, match them with the names declared by the callee.
337  // In case of additional argument not declared by the callee (no match):
338  // - If strict without ellipsis (...) then an error is raised
339  // - otherwise if mode auto then a variable is created.
340  // - otherwise the additional argument is ignored
341  for (size_t i = argcount; i < (argcount + (2 * named_argcount)); i+=2)
342  {
343  RexxString *argName = (RexxString *)arglist[i];
344  RexxObject *argValue = arglist[i+1];
345  bool matched = expectedNamedArguments.match(argName, argValue, this->strictChecking && !this->variableSize);
346  if (!matched && this->autoCreation)
347  {
348  context->traceResult(argValue); // trace if necessary
349  RexxVariableBase *retriever = OREF_NULL;
350  if (argName != OREF_NULL) retriever = RexxVariableDictionary::getVariableRetriever(argName);
351  if (retriever == OREF_NULL || argName->getChar(0) == '.' || isdigit((int)argName->getChar(0)))
352  {
353  RexxString *error = argName->concatToCstring("Expected a symbol for the named argument name; found \"");
354  ProtectedObject p(error);
355  error = error->concatWithCstring("\"");
356  p = error;
358  }
359  // a variable having already a value is not overwritten by an auto named argument
360  if (!retriever->exists(context)) retriever->assign(context, stack, argValue);
361  }
362  }
363 
364  // Now that we have matched each named argument of the caller, we can decide if a default value on callee side is needed.
365  // There is no evaluation of a default value when a value has been provided by the caller.
366  // The order of evaluation is the order of declaration in USE NAMED ARG (left-to-right).
367  // The automatic variables are already created and can be used during the evaluation of a default value.
368  for (size_t i = 0; i < this->variableCount; i++)
369  {
370  RexxVariableBase *variable = this->variables[i].variable;
371  NamedArgument &namedArgument = expectedNamedArguments[i];
372  if (namedArgument.assigned)
373  {
374  RexxObject *argValue = namedArgument.value;
375  context->traceResult(argValue); // trace if necessary
376  variable->assign(context, stack, argValue);
377  }
378  else
379  {
380  // grab a potential default value
381  RexxObject *defaultValue = this->variables[i].defaultValue;
382 
383  // and omitted argument is only value if we've marked it as optional
384  // by giving it a default value
385  if (defaultValue != OREF_NULL)
386  {
387  // evaluate the default value now
388  defaultValue = defaultValue->evaluate(context, stack);
389  context->traceResult(defaultValue); // trace if necessary
390  // assign the value
391  variable->assign(context, stack, defaultValue);
392  stack->pop(); // remove the value from the stack
393  }
394  else
395  {
396  if (!this->strictChecking)
397  {
398  // not doing strict checks, revert to old rules and drop the variable.
399  variable->drop(context);
400 
401  }
402  else
403  {
404  if (context->inMethod())
405  {
406  reportException(Error_Incorrect_method_noarg, OREF_named, variable->getName());
407  }
408  else
409  {
410  reportException(Error_Incorrect_call_noarg, context->getCallname(), OREF_named, variable->getName());
411  }
412  }
413  }
414  }
415  }
416 
417  context->pauseInstruction(); // do debug pause if necessary
418 }
419 
420 
421 // Helper to check that the abbreviated argument names are distinct from each other.
422 // This check is made at parse_time, not at run_time.
424 {
425  if (this->variableCount < 2) return; // 0 or 1 name argument, nothing to check
426 
427  // Helper storage to detect the collisions of names
428  NamedArguments expectedNamedArguments(this->variableCount);
429 
430  // Iterate over the named arguments declared by the callee with the instruction USE NAMED ARG.
431  // Collect their names.
432  for (size_t i = 0; i < this->variableCount; i++)
433  {
434  // Remember: code at risk! I don't use the constructor, and that could bring troubles
435  // as I had when I added nameLength and forgot to put a value here...
436  RexxVariableBase *variable = this->variables[i].variable;
437  expectedNamedArguments[i].name = variable->getName()->getStringData();
438  expectedNamedArguments[i].nameLength = variable->getName()->getLength();
439  }
440 
441  // Iterate over the collected named arguments, and check each one with all the followings.
442  // If there is a match (other than with a stem name) then an error is raised : the names are not unique
443  // Examples:
444  // {use named arg normalization(1), nfl(2)}
445  // {use named arg nfl(2), normalization(1)}
446  for (size_t i = 0; i < this->variableCount; i++)
447  {
448  bool matched = expectedNamedArguments.match(expectedNamedArguments[i].name, expectedNamedArguments[i].nameLength, OREF_NULL, false, i+1, /*parse_time*/ true);
449  // if we reach here with match==true, it's a match that doesn't raise an error (typically a match with a stem name).
450  if (matched) continue; // just to be explicit : here, a matched name is not an error
451  }
452 }
453 
454 
455 /*============================================================================*/
456 /* Named argument helpers for internal methods */
457 /*============================================================================*/
458 
459 void NamedArguments::match(RexxObject **namedArglist, size_t namedArgCount, bool strict, bool extraAllowed, size_t minimumRequired)
460 {
461  // strict checking means we need to enforce min/max limits
462  if (strict)
463  {
464  // not enough of the required arguments? That's an error
465  if (namedArgCount < minimumRequired)
466  {
467  reportException(Error_Incorrect_method_minarg, OREF_named, minimumRequired);
468  }
469  // potentially too many?
470  if (!extraAllowed && namedArgCount > this->count)
471  {
473  }
474  }
475 
476  // Iterate over the named arguments passed by the caller, match them with the names declared by the callee.
477  // In case of additional argument not declared by the callee (no match):
478  // - If strict and not extraAllowed then an error is raised
479  // - otherwise the additional argument is ignored.
480  for (size_t i= 0; i < (2 * namedArgCount); i+=2)
481  {
482  RexxString *argName = (RexxString *)namedArglist[i];
483  RexxObject *argValue = namedArglist[i+1];
484  this->match(argName, argValue, strict && !extraAllowed);
485  }
486 
487  // Now that we have matched each named argument of the caller, we can check if some mandatory arguments are missing.
488  if (strict)
489  {
490  for (size_t i = 0; i < this->count; i++)
491  {
492  NamedArgument &namedArgument = (*this)[i];
493  if (!namedArgument.assigned && namedArgument.value == OREF_NULL)
494  {
495  reportException(Error_Incorrect_method_noarg, OREF_named, namedArgument.name);
496  }
497  }
498  }
499 }
500 
501 
502 /*
503 Store the value of the named argument in the right box, if recognized (abbreviation supported)
504 Assumption: you will not call this helper with the same name twice, because once a name has been matched, it is skipped.
505 Example:
506  // USE NAMED ARG ITEM(2), INDEX(2)=0, MAXDEPTH(1)=10
507  NamedArguments namedArguments(3);
508  namedArguments[0] = NamedArgument("ITEM", 2, OREF_NULL); // At least 2 characters, no default value
509  namedArguments[1] = NamedArgument("INDEX", 2, IntegerZero); // At least 2 characters, default value = 0
510  namedArguments[2] = NamedArgument("MAXDEPTH", 1, IntegerTen); // At least 1 character, default value = 10
511  // For each named argument passed by the caller
512  namedArguments.match(name1, strlen(name1), value1);
513  namedArguments.match(name2, strlen(name2), value2);
514  namedArguments.match(name3, strlen(name3), value3);
515 */
516 
517 bool NamedArguments::match(RexxString *name, RexxObject *value, bool strict, size_t from, bool parse_time)
518 {
519  if (name == NULL) return false;
520  return this->match(name->getStringData(), name->getLength(), value, strict, from, parse_time);
521 }
522 
523 void nameCollision(const char *name1, const char *name2)
524 {
525  char buffer[256];
526 
527  // Report the shortest name first.
528  // Some examples where a swap is needed:
529  // {use named arg commands, command} --> The name 'COMMAND' collides with 'COMMANDS'
530  ssize_t ml1 = strlen(name1);
531  ssize_t ml2 = strlen(name2);
532  if (ml1 > ml2)
533  {
534  std::swap(name1, name2);
535  }
536 
537  if (strcmp(name1, name2) == 0)
538  {
539  Utilities::snprintf(buffer, sizeof buffer - 1, "Use named arg: The name '%s' is declared more than once", name1);
540  }
541  else
542  {
543  Utilities::snprintf(buffer, sizeof buffer - 1, "Use named arg: The name '%s' collides with '%s'", name1, name2);
544  }
546 }
547 
548 bool NamedArguments::match(const char *name, size_t nameLength, RexxObject *value, bool strict, size_t from, bool parse_time)
549 {
550  if (name == NULL) return false;
551 
552  // There is no order for the named argument, so try all the expected names.
553  // At parse_time, called for the instruction use named arg:
554  // 'from' will be the index+1 of the current name being checked for collision.
555  // Ex: for "use named arg a,b,c" will be called for 'a' (check ab ac) and for 'b' (check bc)
556  // At run_time, called for the list of named arguments passed by the caller:
557  // 'from' will be always 0. Ex: for "f(b:1, d:3)" will be called for 'b' (check ba bb) and 'd' (check da db dc)
558 
559  size_t i = 0;
560  bool matched = false;
561  for (i = from; i < this->count; i++)
562  {
563  matched = NamedArguments::checkNameMatching(name, nameLength, i, parse_time);
564  if (matched) break;
565  }
566 
567  if (matched)
568  {
569  if (parse_time) nameCollision(name, this->namedArguments[i].name);
570  else
571  {
572  if (value != OREF_NULL) this->namedArguments[i].value = value;
573  this->namedArguments[i].assigned = true;
574  }
575  return true;
576  }
577 
578  // The name did not match an expected argument name
579  if (strict)
580  {
581  RexxString *rexxname = new_string(name);
582  ProtectedObject p(rexxname);
583  reportException(Error_Invalid_argument_general, OREF_named, rexxname, "is not an expected argument name");
584  }
585  return false;
586 }
587 
588 
589 bool NamedArguments::checkNameMatching(const char *name, size_t nameLen, size_t i, bool parse_time)
590 {
591  if (this->namedArguments[i].assigned) return false; // Already matched (assumption: you will not call this helper with the same name twice)
592  const char *expectedName = this->namedArguments[i].name;
593  return strcmp(name, expectedName) == 0;
594 }
void reportException(wholenumber_t error)
#define OREF_NULL
Definition: RexxCore.h:61
#define OrefSet(o, r, v)
Definition: RexxCore.h:101
#define Error_Incorrect_call_noarg
#define Error_Incorrect_call_minarg
#define Error_Incorrect_method_maxarg
#define Error_Invalid_argument_general
#define Error_Incorrect_method_minarg
#define Error_Incorrect_call_maxarg
#define Error_Incorrect_method_noarg
#define Error_Translation_user_defined
#define Error_Symbol_expected_user_defined
#define memory_mark(oref)
Definition: RexxMemory.hpp:450
#define flatten_reference(oref, envel)
Definition: RexxMemory.hpp:498
#define memory_mark_general(oref)
Definition: RexxMemory.hpp:451
#define cleanUpFlatten
Definition: RexxMemory.hpp:484
#define setUpFlatten(type)
Definition: RexxMemory.hpp:478
RexxString * new_string(const char *s, stringsize_t l)
void nameCollision(const char *name1, const char *name2)
NamedArgument * namedArguments
Definition: RexxCore.h:557
const size_t count
Definition: RexxCore.h:552
void match(RexxObject **namedArglist, size_t namedArgCount, bool strict, bool extraAllowed, size_t minimumRequired=0)
bool checkNameMatching(const char *name, size_t nameLength, size_t i, bool parse_time)
RexxString * getCallname()
size_t getMethodArgumentCount()
void traceResult(RexxObject *v)
void traceInstruction(RexxInstruction *v)
RexxObject ** getMethodArgumentList()
size_t getMethodNamedArgumentCount()
RexxInstruction * nextInstruction
void execute(RexxActivation *, RexxExpressionStack *)
RexxInstructionUseStrict(size_t, bool, bool, bool, bool, RexxQueue *, RexxQueue *)
void executeNamedArguments(RexxActivation *, RexxExpressionStack *)
RexxObject * getArgument(RexxObject **arglist, size_t count, size_t target)
void executePositionalArguments(RexxActivation *, RexxExpressionStack *)
virtual RexxObject * evaluate(RexxActivation *, RexxExpressionStack *)
RexxObject * pop()
Definition: QueueClass.hpp:80
size_t getLength()
const char * getStringData()
RexxString * concatWithCstring(const char *)
RexxString * concatToCstring(const char *)
char getChar(size_t p)
virtual bool exists(RexxActivation *)
virtual void assign(RexxActivation *, RexxExpressionStack *, RexxObject *)
virtual RexxString * getName()
virtual void drop(RexxActivation *)
static RexxVariableBase * getVariableRetriever(RexxString *variable)
RexxVariableBase * variable
RexxObject * defaultValue
static int snprintf(char *buffer, size_t count, const char *format,...)
bool assigned
Definition: RexxCore.h:516
const char * name
Definition: RexxCore.h:513
RexxObject * value
Definition: RexxCore.h:515
SSIZE_T ssize_t