Blog

Is Eval Evil or not?

April 29, 2015

Though many authors try to convince us that Eval is Evil, I would personally disagree. I believe Eval operator in Javascript is a great and sophisticated tool for sophisticated and responsible developers. I believe you have probably seen that picture which claims that with Great Beard Comes Great Responsibility.

unnamed
So Eval operator is not evil or good, it is just a tool without any ethical color like a scalpel in hands of a surgeon. Definitely you should use it in the right way. Information for linguists: “Eval” is just a shortened form of an “evaluate” verb, developers are generally lazy and tend to shorten everything which is possible to shorten, especially they like to shorten words, so they always use some brief combination of letters to save time.

There were several discussions about existence of functionality in Salesforce Apex language which would allow developers to execute code like JavaScript ‘eval’ operator ([1], [2]).
Obviously there is no Eval operator in Apex. However, some people have invented some options to mimic a Javascript eval() in Apex (in the links above).
So, the first option was to use ToolingAPI library’s executeAnonymousUnencoded method and throw some custom exception and catch it to receive the result, approach described here and here. The second link looks the same as the site from the first link. Probably it has been moved from Wordpress blog to separate domain. This approach is followed by this code sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Dynamic {
public class IntentionalException extends Exception{}
public static boolean eval(String toEval){
boolean result = false;
if(!toEval.startsWith(‘if’)) {
toEval = ‘if(‘ + toEval + ‘) {throw new Dynamic.IntentionalException(\’true\’);} else {throw new Dynamic.IntentionalException(\’false\’);}’;
}
ToolingAPI x = new ToolingAPI();
try{
ToolingAPI.ExecuteAnonymousResult toolingResult = x.executeAnonymousUnencoded(toEval);
} catch (IntentionalException ie) {
result = (ie.getMessage() == ‘true’) ? True : False;
}
return result;
}
}

Another approach invented by Daniel Ballinger is to parse apex debug logs to retrieve results. To use this approach you don’t need ToolingAPI library but you require his wrapper class. Having it you may run one of his examples.

To evaluate concatenation of strings you may use the following example:

1
2
3
4
string output = soapSforceCom200608Apex.evalString(‘string first = \’foo\’; string second = \’bar\’; string result = first + second; System.debug(LoggingLevel.Error, result);’);
System.assertEquals(‘foobar’, output);
System.debug(LoggingLevel.ERROR, ‘ output= ‘+ output);

To evaluate sum of integers you may use the following example:

1
2
3
4
integer output = soapSforceCom200608Apex.evalInteger(‘integer first = 1; integer second = 5; integer result = first + second; System.debug(LoggingLevel.Error, result);’);
System.assertEquals(6, output);
System.debug(LoggingLevel.ERROR, ‘ output= ‘+ output);

To evaluate logical disjunction of boolean values you may use the following example:

1
2
3
4
boolean output = soapSforceCom200608Apex.evalBoolean(‘boolean first = true; boolean second = false; boolean result = first || second; System.debug(LoggingLevel.Error, result);’);
System.assertEquals(true, output);
System.debug(LoggingLevel.ERROR, ‘ output= ‘+ output);

To evaluate JSON serialization of object you may use the following example:

1
2
3
4
5
6
7
string outputJson = soapSforceCom200608Apex.evalString(‘List<object> result = new List<object>(); result.add(\’foo\’); result.add(12345); System.debug(LoggingLevel.Error, JSON.serialize(result));’);
System.debug(LoggingLevel.ERROR, ‘ outputJson= ‘+ outputJson);
List<Object> result = (List<Object>)JSON.deserializeUntyped(outputJson);
System.assertEquals(2, result.size());
System.assertEquals(‘foo’, result[0]);
System.assertEquals(12345, result[1]);

To run examples using any approach described above you need Author Apex permission (included in standard System Administrator profile), access to tooling API or SOAP API (basically if you are an administrator you should have this access) and you need to add URL of your own organization to Remote Site settings. You may find your URL in browser address bar or by running anonymous apex code

1
2
System.debug( LoggingLevel.ERROR, ‘ url= ‘+ ( URL.getSalesforceBaseUrl().toExternalForm()  + ‘/services/Soap/s/31.0’ ) );

I was very impressed when I discovered that there is a possibility to mimic a Javascript eval operator in Apex. Hope you too. Eval is definitely not a root of all Evil. Enjoy. 🙂

Tags



Share


3 replies
  1. Jyothsna
    Jyothsna says:

    At one of my code, I need to convert String of Expression to Boolean. Below is my code.
    Boolean a = true;
    Boolean b = false;
    Boolean c = true;
    Boolean d = false;
    String s = ‘(true || false) &&(true || false)’;
    Boolean b = soapSforceCom200608Apex.evalBoolean(s);

    The above coding is throughing error as EXCEPTION: System.AssertException: Assertion Failed: expecting a semi-colon, found ”

    If it is not working like this how come this function is same as eval() in javascript.
    If soapSforceCom200608Apex.evalBoolean accepts only apex code as input. How is it useful?

    Reply
    • Bohdan Dovhan
      Bohdan Dovhan says:

      Hi Jyothsna.
      Probably I didn’t state this clearly in this article that there is no actual ‘Eval’ operator in Apex, but many developers try to mimic ‘Eval’ operator functionality using different options.
      So to use Daniel Ballinger’s approach to evaluate String ‘(true || false) &&(true || false)’ one can try the following snippet:

      String s = ‘Boolean result = (true || false) &&(true || false); System.debug(LoggingLevel.Error, result);’;
      Boolean b = soapSforceCom200608Apex.evalBoolean(s);
      System.debug(LoggingLevel.ERROR, ‘b= ‘+ b);

      or

      String s = ‘Boolean a = true;Boolean b = false;Boolean c = true;Boolean d = false;Boolean result = (a || b) &&(c || d); System.debug(LoggingLevel.Error, result);’;
      Boolean b = soapSforceCom200608Apex.evalBoolean(s);
      System.debug(LoggingLevel.ERROR, ‘b= ‘+ b);

      Both code shippets I provide work for me.
      I was also curious if it may be possible to pass some parameters somehow and asked Daniel about this in comments under his article and he confirmed that “Unfortunately, unlike Javascript, all the required parameters need to be encoded into the input string. No state will be available in the eval unless it is explicitly passed in.”

      The main idea here is not to get some functionality to evaluate expression but rather to run dynamic code which may evaluate some expression inside.

      On the other hand, if you try to use Kevin’s ( CodeFriar’s ) approach, you actually can pass expressions like this:

      Boolean b = Dynamic.eval( ‘(true || false) &&(true || false)’);
      System.debug(LoggingLevel.ERROR, ‘b= ‘+ b);

      but still you cannot pass variables or parameters to it.

      I find this useful since this may run some dynamic code.
      For example, if you want generate some code to execute dynamically.

      For areas of applications you can also refer to original Daniel’s and Kevin’s articles.
      Daniel has listed such areas of applications in his article ( in Benefits section):
      1. Where a rollback may be required the separate context via the callout doesn’t require the invoking Apex to rollback. You can still progress and make further callouts and subsequent DML operations.
      2. JSON can be used in the response message to return structured data.
      3. An odd way of increasing limits. E.g. Each anonymous apex context gets a new set of limits.
      4. You can handle classes of exception that would otherwise be uncatchable, such as System.LimitException and System.ProcedureException
      Kevin invented his version of eval when he tried to parse JEXL expression strings in Apex.
      However, I believe there might be many more areas of application.

      Reply
    • Bohdan Dovhan
      Bohdan Dovhan says:

      By the way, there is one new feature in Salesforce called Platform Cache. If we use it I think we can find a way to pass parameters to our eval functions, you can try to setup Platform Cache partition and call it default and then you can try to execute the following code:

      Cache.OrgPartition orgPartition = Cache.Org.getPartition(‘default’);

      orgPartition.put(‘a’, true);
      orgPartition.put(‘b’, false);
      orgPartition.put(‘c’, true);
      orgPartition.put(‘d’, false);

      String s = ‘Cache.OrgPartition orgPartition = Cache.Org.getPartition(\’default\’);Boolean a = Boolean.valueOf(orgPartition.get(\’a\’));Boolean b = Boolean.valueOf(orgPartition.get(\’b\’));Boolean c = Boolean.valueOf(orgPartition.get(\’c\’));Boolean d = Boolean.valueOf(orgPartition.get(\’d\’));Boolean result = (a || b) &&(c || d); System.debug(LoggingLevel.Error, result);’;
      Boolean b = soapSforceCom200608Apex.evalBoolean(s);
      System.debug(LoggingLevel.ERROR, ‘b= ‘+ b);

      Would do you think, Jyothsna? Does this answer your question?

      Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Articles

Part 2: Optimizing and scaling microservices. Organic growth of eco-systems.

October 11, 2017 | Nikola Krastev

A microservices approach is not a silver bullet for all software architecture problems. It introduces tradeoffs and challenges of its own. However, process gains and improvements in human performance have been considered to be worth the overhead in technology. Here are some general arguments against using sophisticated SOA. Server performance and overhead in communication By […]

CoreValue President at IT Arena 2017

September 27, 2017

We are pleased to announce that CoreValue president Igor Kruglyak will participate in IT Arena 2017, to be held in Lviv, Ukraine, September 29 – October 1, 2017.