Bug #6458
ensure that OO expressions resolve to query substitution parameters, like handle-based method calls do
100%
History
#1 Updated by Eric Faulhaber almost 2 years ago
We need to create test cases with OO expressions in the WHERE clause of a query/FIND/FOR/etc., which does not reference the current buffer. These should convert such that the OO expression is refactored into a query substitution parameter.
Constantin, I understand you discovered a problem with this. Do you already have a test case to start with?
#2 Updated by Eric Faulhaber about 1 year ago
- Assignee set to Boris Schegolev
Constantin, Greg informed me that you were concerned about certain OO expressions embedded in WHERE clauses not converting properly to query substitution parameters.
Can you please help Boris scope/understand this task?
#3 Updated by Constantin Asofiei about 1 year ago
I think the issue is that we don't emit as lambda the OO expression. Take this test:
for each tt1 where tt1.f1 = func1(1): message "d" tt1.f1. end.
which converts in FWD like:
forEach("loopLabel3", new Block((Init) () -> { query3.initialize(tt1, "tt1.f1 = ?", null, "tt1.recid asc", new Object[] { (P2JQuery.Parameter) () -> func1(new integer(1)) }); },
Note how a lambda is emitted.
Now, for OO case:
for each tt1 where tt1.f1 = oo.Foo:m2(1): message "a" tt1.f1. end.
it converts like:
forEach("loopLabel0", new Block((Init) () -> { query0.initialize(tt1, "tt1.f1 = ?", null, "tt1.recid asc", new Object[] { com.goldencode.testcases.oo.Foo.m2(new integer(1)) }); },
Note how we don't wrap this in a lambda parameter, and the evaluation is done only once.
From this, other tests where instance methods or both operands are OO calls can be written.
#4 Updated by Boris Schegolev 12 months ago
Gentlemen, I couldn't figure out details of the conversion procedure. So, anyone who has time and knowledge is more than welcome to enlighten me :)
What I know/did so far:
I created test cases to simulate the difference as described above. I understand the conversion goes through stages (preprocessor, lexer, parser, ASTs) and results of individual stages are stored next to the original *.p
file as *.lexer
, *.parser
, *.jast
, etc. The stages are defined in ConversionDriver
and more steps in TransformDriver
.
Then there are rule sets in /rules/
, but I am not sure how they participate in the conversion. Are they only used for code validation/analysis (incorrect code triggers rules) or they actually generate the resulting code at some point?
Anyway, I can see that *.jast
file already contains the transformed code including the difference described in this task. What I don't understand is what part of our code decided this difference should be there. Any guidance greatly appreciated!
#5 Updated by Constantin Asofiei 12 months ago
Boris, first, run conversion with -Drules.tracing=true
. Use this for both the OO and non-OO tests.
You can look at the .ast and .jast file for the non-OO test, and there will be annotations which have the byRule
text in them - this will specify which TRPL rule (from rules/
) was responsible for emitting that AST node. So, looking at the lambda AST in the .jast file, you can find the .rules file which generated this. You can analyze and backtrack from there.
Otherwise, the TRPL code in rules/
folder is responsible for interpreting the parsed .ast, annotating it, and finally generating the .jast and the .java code.
#8 Updated by Eric Faulhaber 12 months ago
- Priority changed from Urgent to Normal
#9 Updated by Boris Schegolev 12 months ago
- File conversion.log added
I am running a simple conversion for OO mode. The class itself is simple:
using oo.*. class oo.Foo: method public static int func1(): return 1. end. end class.
I end up with an error though. There are some nulls
reported and then a rule is triggered:
Null annotation (full-java-class) for Foo [CLASS_NAME] @3:43 (309237645338) Null annotation (simple-java-class) for Foo [CLASS_NAME] @3:43 (309237645338) Null annotation (containing-package) for Foo [CLASS_NAME] @3:43 (309237645338) Null annotation (found-in-full-java-class) for func1 [OO_METH_INT] @3:47 (309237645339) com.goldencode.p2j.pattern.TreeWalkException: ERROR! Active Rule: ----------------------- RULE REPORT ----------------------- Rule Type : POST Source AST: [ block ] BLOCK/ @0:0 {309237645313} Copy AST : [ block ] BLOCK/ @0:0 {309237645313} Condition : persist() Loop : false --- END RULE REPORT ---
This only happens for classes, *.p
conversions work just fine. Any suggestions greatly appreciated! Full conversion log attached.
#10 Updated by Constantin Asofiei 12 months ago
You must include Foo.cls
in your conversion list.
#12 Updated by Greg Shah 12 months ago
It is parsed because it is referenced elsewhere. As I've noted previously via email, just because it is parsed doesn't mean that it is added automatically to the conversion list. At this time you MUST explicitly include every non-skeleton class/interface/enum in the referenced object graphs of all converted classes or procedures. See #6082 for the task where we fix this. Until then, add everything referenced.
#14 Updated by Constantin Asofiei 11 months ago
The change looks good. Please post a sample of the 4GL and generated code where the OO method is involved.
#16 Updated by Boris Schegolev 11 months ago
The 4GL code was simple like:
using oo.*. for each test_tab1 where test_tab1.col2 = Foo:func1(): message "d" test_tab1.col2. end.
It newly translates to:
@LegacySignature(type = Type.MAIN, name = "do2.p") public void execute() { externalProcedure(Do2.this, new Block((Body) () -> { AdaptiveQuery query0 = new AdaptiveQuery(); forEach("loopLabel0", new Block((Init) () -> { RecordBuffer.openScope(testTab1); query0.initialize(testTab1, "testTab1.col2 = ?", null, "testTab1.recid asc", new Object[] { (P2JQuery.Parameter) () -> com.goldencode.testcases.oo.Foo.func1() }); }, (Body) () -> { query0.next(); message(new Object[] { "d", (integer) new FieldReference(testTab1, "col2").getValue() }); })); })); }
Branch is rebased, ready to merge.
#17 Updated by Constantin Asofiei 11 months ago
Please do some tests with an instance function instead of static, parameters (simple vars, more complex like a function call, etc), call via THIS-OBJECT:func1()
. I'm pretty sure this should work, but please double-check.
#18 Updated by Boris Schegolev 11 months ago
Seems to work just as well:
public void execute() { externalProcedure(Do3.this, new Block((Init) () -> { ObjectOps.register(inst); }, (Body) () -> { inst.assign(ObjectOps.newInstance(com.goldencode.testcases.oo.Foo.class)); AdaptiveQuery query0 = new AdaptiveQuery(); forEach("loopLabel0", new Block((Init) () -> { RecordBuffer.openScope(testTab1); query0.initialize(testTab1, "testTab1.col2 = ?", null, "testTab1.recid asc", new Object[] { (P2JQuery.Parameter) () -> inst.ref().func2(new integer(1)) }); }, (Body) () -> { query0.next(); message(new Object[] { "d", (integer) new FieldReference(testTab1, "col2").getValue() }); })); })); }
#19 Updated by Constantin Asofiei 11 months ago
Thanks, looks good to merge to trunk.