CompileMisc.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. package gnu.xquery.util;
  2. import gnu.expr.*;
  3. import gnu.mapping.Procedure;
  4. import gnu.bytecode.*;
  5. import gnu.kawa.functions.NumberCompare;
  6. import gnu.kawa.xml.*;
  7. import gnu.math.*;
  8. import gnu.xquery.lang.XQuery;
  9. import gnu.kawa.functions.AddOp;
  10. import gnu.kawa.functions.NumberCompare;
  11. import gnu.kawa.functions.ValuesMap;
  12. import gnu.kawa.reflect.CompileReflect;
  13. import gnu.kawa.reflect.OccurrenceType;
  14. public class CompileMisc
  15. {
  16. /** Inliner for the Compare procedure. */
  17. public static Expression validateCompare
  18. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  19. {
  20. exp.visitArgs(visitor);
  21. Expression folded = exp.inlineIfConstant(proc, visitor);
  22. if (folded != exp)
  23. return folded;
  24. Compare cproc = (Compare) proc;
  25. if ((cproc.flags & Compare.VALUE_COMPARISON) != 0)
  26. {
  27. }
  28. else
  29. {
  30. exp = new ApplyExp(ClassType.make("gnu.xquery.util.Compare")
  31. .getDeclaredMethod("apply", 4),
  32. new Expression[] { new QuoteExp(IntNum.make(cproc.flags)),
  33. exp.getArg(0),
  34. exp.getArg(1),
  35. QuoteExp.nullExp });
  36. }
  37. if (exp.getTypeRaw() == null)
  38. exp.setType(XDataType.booleanType);
  39. return exp;
  40. }
  41. /** Inliner for the BooleanValue procedure. */
  42. public static Expression validateBooleanValue
  43. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  44. {
  45. exp.visitArgs(visitor);
  46. Expression[] args = exp.getArgs();
  47. if (args.length == 1)
  48. {
  49. Expression arg = args[0];
  50. Type type = arg.getType();
  51. if (type == XDataType.booleanType)
  52. return arg;
  53. if (type == null)
  54. exp.setType(XDataType.booleanType);
  55. if (arg instanceof QuoteExp)
  56. {
  57. Object value = ((QuoteExp) arg).getValue();
  58. try
  59. {
  60. return BooleanValue.booleanValue(value) ? XQuery.trueExp : XQuery.falseExp;
  61. }
  62. catch (Exception ex)
  63. {
  64. String message = "cannot convert to a boolean";
  65. visitor.getMessages().error('e', message);
  66. return new ErrorExp(message);
  67. }
  68. }
  69. }
  70. return exp;
  71. }
  72. /** Inliner for the ArithOp procedure. */
  73. public static Expression validateArithOp
  74. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  75. {
  76. exp.visitArgs(visitor);
  77. // FUTURE
  78. return exp;
  79. }
  80. /** Inliner for the {@link ValuesFilter} procedure. */
  81. public static Expression validateApplyValuesFilter
  82. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  83. {
  84. ValuesFilter vproc = (ValuesFilter) proc;
  85. exp.visitArgs(visitor); // FIXME - be smarter about type propagation
  86. Expression[] args = exp.getArgs();
  87. Expression exp2 = args[1];
  88. LambdaExp lexp2;
  89. if (! (exp2 instanceof LambdaExp)
  90. || (lexp2 = (LambdaExp) exp2).min_args != 3
  91. || lexp2.max_args != 3)
  92. return exp;
  93. Expression seq = args[0];
  94. Type seqType = seq.getType();
  95. if (seqType instanceof OccurrenceType) {
  96. OccurrenceType occType = (OccurrenceType) seqType;
  97. Type baseType = occType.getBase();
  98. if (OccurrenceType.itemCountIsOne(baseType)) {
  99. int min = occType.minOccurs();
  100. if (min > 0)
  101. occType =
  102. new OccurrenceType(baseType, min, occType.maxOccurs());
  103. exp.setType(occType);
  104. }
  105. }
  106. Compilation comp = visitor.getCompilation();
  107. Declaration dotArg = lexp2.firstDecl();
  108. Declaration posArg = dotArg.nextDecl();
  109. Declaration lastArg = posArg.nextDecl();
  110. dotArg.setCanRead(true);
  111. posArg.setCanRead(true);
  112. lexp2.setInlineOnly(exp, visitor.getCurrentLambda());
  113. // Splice out lastArg
  114. lexp2.remove(posArg, lastArg);
  115. lexp2.min_args = 2;
  116. lexp2.max_args = 2;
  117. if (! lastArg.getCanRead() && vproc.kind != 'R')
  118. {
  119. // Don't need to do anything more - lastArg is not needed.
  120. return exp;
  121. }
  122. lastArg.setCanRead(true);
  123. comp.letStart();
  124. Method sizeMethod;
  125. if (vproc.kind == 'P')
  126. {
  127. sizeMethod = Compilation.typeValues.getDeclaredMethod("countValues", 1);
  128. }
  129. else
  130. {
  131. seqType = SortNodes.typeSortedNodes;
  132. seq = new ApplyExp(SortNodes.sortNodes, new Expression [] {seq});
  133. sizeMethod = ClassType.make("gnu.lists.AbstractSequence")
  134. .getDeclaredMethod("size", 0);
  135. }
  136. Declaration sequence = comp.letVariable("sequence", seqType, seq);
  137. comp.letEnter();
  138. Expression pred = lexp2.body;
  139. Type predType = lexp2.body.getType();
  140. if (predType != XDataType.booleanType) // Overly conservative, but simple.
  141. pred = new ApplyExp(vproc.matchesMethod,
  142. new Expression[] { pred,
  143. new ReferenceExp(posArg) });
  144. if (vproc.kind == 'R')
  145. {
  146. Declaration posIncoming = new Declaration(null, Type.intType);
  147. posIncoming.setCanRead(true);
  148. Expression init
  149. = new ApplyExp(AddOp.MINUS,
  150. new Expression[] {
  151. new ReferenceExp(lastArg),
  152. new ReferenceExp(posIncoming)});
  153. init
  154. = new ApplyExp(AddOp.PLUS,
  155. new Expression[] {
  156. init,
  157. new QuoteExp(IntNum.one())});
  158. comp.letStart();
  159. lexp2.replaceFollowing(dotArg, posIncoming);
  160. comp.letVariable(posArg, init);
  161. comp.letEnter();
  162. pred = comp.letDone(pred);
  163. }
  164. pred = new IfExp(pred,
  165. new ReferenceExp(dotArg),
  166. QuoteExp.voidExp);
  167. lexp2.body = pred;
  168. ApplyExp doMap
  169. = new ApplyExp(ValuesMap.valuesMapWithPos,
  170. new Expression[] { lexp2,
  171. new ReferenceExp(sequence) });
  172. doMap.setType(OccurrenceType.getInstance(dotArg.getType(), 0, -1));
  173. lexp2.returnContinuation = doMap;
  174. Expression lastInit = new ApplyExp(sizeMethod,
  175. new Expression[] {
  176. new ReferenceExp(sequence)});
  177. comp.letStart();
  178. comp.letVariable(lastArg, lastInit);
  179. LetExp let2 = comp.letDone(gnu.kawa.functions.CompileMisc.validateApplyValuesMap(doMap, visitor, required, ValuesMap.valuesMapWithPos));
  180. return comp.letDone(let2);
  181. }
  182. public static Expression validateApplyRelativeStep
  183. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  184. {
  185. // FIXME make use of type of E1 to set dot in E2.
  186. exp.visitArgs(visitor);
  187. Expression[] args = exp.getArgs();
  188. Expression exp1 = args[0];
  189. Expression exp2 = args[1];
  190. LambdaExp lexp2;
  191. Compilation comp = visitor.getCompilation();
  192. if (! (exp2 instanceof LambdaExp)
  193. // The following optimization breaks when interpreting, because
  194. // then CoerceToNodes may not work.
  195. || ! comp.mustCompile
  196. || (lexp2 = (LambdaExp) exp2).min_args != 3
  197. || lexp2.max_args != 3)
  198. return exp;
  199. lexp2.setInlineOnly(exp, visitor.getCurrentLambda());
  200. exp2 = lexp2.body;
  201. Declaration dotArg = lexp2.firstDecl();
  202. Declaration posArg = dotArg.nextDecl();
  203. Declaration lastArg = posArg.nextDecl();
  204. // Splice out the "last" argument - we'll move it out.
  205. // The remaining two arguments are suitable for a ValuesMap.
  206. posArg.setNext(lastArg.nextDecl());
  207. lastArg.setNext(null);
  208. lexp2.min_args = 2;
  209. lexp2.max_args = 2;
  210. Type type1 = exp1.getType();
  211. if (type1 != null &&NodeType.anyNodeTest.compare(type1) == -3)
  212. {
  213. Language language = visitor.getCompilation().getLanguage();
  214. String message = "step input is "+language.formatType(type1)+" - not a node sequence";
  215. visitor.getMessages().error('e', message);
  216. return new ErrorExp(message);
  217. }
  218. Type rtype = exp.getTypeRaw();
  219. Type rtypePrime;
  220. int nodeCompare;
  221. if (rtype == null || rtype == Type.pointer_type)
  222. {
  223. Type type2 = exp2.getType();
  224. rtypePrime = OccurrenceType.itemPrimeType(type2);
  225. nodeCompare = NodeType.anyNodeTest.compare(rtypePrime);
  226. if (nodeCompare >= 0)
  227. rtype = NodeSetType.getInstance(rtypePrime);
  228. else
  229. rtype = OccurrenceType.getInstance(rtypePrime, 0, -1);
  230. exp.setType(rtype);
  231. }
  232. if (lastArg.getCanRead())
  233. {
  234. ClassType typeNodes = CoerceNodes.typeNodes;
  235. comp.letStart();
  236. Declaration sequence
  237. = comp.letVariable(null, typeNodes,
  238. new ApplyExp(CoerceNodes.coerceNodes,
  239. new Expression [] { exp1 }));
  240. comp.letEnter();
  241. Method sizeMethod = typeNodes.getDeclaredMethod("size", 0);
  242. Expression lastInit
  243. = new ApplyExp(sizeMethod,
  244. new Expression[] {new ReferenceExp(sequence)});
  245. comp.letStart();
  246. comp.letVariable(lastArg, lastInit);
  247. comp.letEnter();
  248. LetExp lastLet =
  249. comp.letDone(new ApplyExp(exp.getFunction(),
  250. new Expression[] { new ReferenceExp(sequence),
  251. lexp2 }));
  252. return comp.letDone(lastLet);
  253. }
  254. ApplyExp result = exp;
  255. // Try to rewrite A/B[P] to (A/B)[P].
  256. // This only works if P doesn't depend in position() or last().
  257. if (exp2 instanceof ApplyExp)
  258. {
  259. ApplyExp aexp2 = (ApplyExp) exp2;
  260. Object proc2 = aexp2.getFunction().valueIfConstant();
  261. Expression vexp2;
  262. if (proc2 instanceof ValuesFilter
  263. && (vexp2 = aexp2.getArgs()[1]) instanceof LambdaExp)
  264. {
  265. LambdaExp lvexp2 = (LambdaExp) vexp2;
  266. Declaration dot2 = lvexp2.firstDecl();
  267. Declaration pos2;
  268. if (dot2 != null && (pos2 = dot2.nextDecl()) != null
  269. && pos2.nextDecl() == null
  270. && ! pos2.getCanRead()
  271. // If the predicate can evaluate to a number, then the
  272. // optimization is unsafe, since we implicitly
  273. // compare against position().
  274. && ClassType.make("java.lang.Number").compare(lvexp2.body.getType()) == -3)
  275. {
  276. exp2 = aexp2.getArg(0);
  277. lexp2.body = exp2;
  278. aexp2.setArg(0, exp);
  279. result = aexp2;
  280. }
  281. }
  282. }
  283. // Now we can rewrite 'descendant-or-self::node()/B' (which is the
  284. // expansion of the abbreviated syntax '//B') to /descendant::B'.
  285. if (exp1 instanceof ApplyExp && exp2 instanceof ApplyExp)
  286. {
  287. ApplyExp aexp1 = (ApplyExp) exp1;
  288. ApplyExp aexp2 = (ApplyExp) exp2;
  289. Object p1 = aexp1.getFunction().valueIfConstant();
  290. Object p2 = aexp2.getFunction().valueIfConstant();
  291. Expression exp12;
  292. if (p1 == RelativeStep.relativeStep && p2 instanceof ChildAxis
  293. && aexp1.getArgCount() == 2
  294. && (exp12 = aexp1.getArg(1)) instanceof LambdaExp)
  295. {
  296. LambdaExp lexp12 = (LambdaExp) exp12;
  297. if (lexp12.body instanceof ApplyExp
  298. && ((ApplyExp) lexp12.body).getFunction().valueIfConstant() == DescendantOrSelfAxis.anyNode)
  299. {
  300. exp.setArg(0, aexp1.getArg(0));
  301. aexp2.setFunction(new QuoteExp(DescendantAxis.make(((ChildAxis) p2).getNodePredicate())));
  302. }
  303. }
  304. }
  305. return result;
  306. }
  307. public static Expression validateApplyOrderedMap
  308. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  309. {
  310. exp.visitArgs(visitor);
  311. Expression[] args = exp.getArgs();
  312. if (args.length > 2)
  313. {
  314. Expression[] rargs = new Expression[args.length-1];
  315. System.arraycopy(args, 1, rargs, 0, rargs.length);
  316. Expression[] xargs = new Expression[2];
  317. Method makeTupleMethod = typeTuples.getDeclaredMethod("make$V", 2);
  318. xargs[0] = args[0];
  319. xargs[1] = new ApplyExp(makeTupleMethod, rargs);
  320. return new ApplyExp(proc, xargs);
  321. }
  322. return exp;
  323. }
  324. static final ClassType typeTuples
  325. = ClassType.make("gnu.xquery.util.OrderedTuples");
  326. public static void compileOrderedMap (ApplyExp exp, Compilation comp, Target target, Procedure proc)
  327. {
  328. Expression[] args = exp.getArgs();
  329. if (args.length != 2)
  330. {
  331. ApplyExp.compile(exp, comp, target);
  332. return;
  333. }
  334. CodeAttr code = comp.getCode();
  335. Scope scope = code.pushScope();
  336. Variable consumer = scope.addVariable(code, typeTuples, null);
  337. args[1].compile(comp, Target.pushValue(typeTuples));
  338. code.emitStore(consumer);
  339. ConsumerTarget ctarget = new ConsumerTarget(consumer);
  340. args[0].compile(comp, ctarget);
  341. Method mm = typeTuples.getDeclaredMethod("run$X", 1);
  342. code.emitLoad(consumer);
  343. PrimProcedure.compileInvoke(comp, mm, target, exp.isTailCall(),
  344. 182/*invokevirtual*/, Type.pointer_type, false);
  345. code.popScope();
  346. }
  347. static final ClassType typeXDataType =
  348. ClassType.make("gnu.kawa.xml.XDataType");
  349. static final Method castMethod = typeXDataType.getDeclaredMethod("cast", 1);
  350. public static Expression validateApplyCastAs
  351. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  352. {
  353. exp.visitArgs(visitor);
  354. exp = CompileReflect.inlineClassName(exp, 0, visitor);
  355. Expression[] args = exp.getArgs();
  356. if (args.length != 2 || ! (args[0] instanceof QuoteExp))
  357. return exp;
  358. Object type = ((QuoteExp) args[0]).getValue();
  359. if (type instanceof XDataType)
  360. return new ApplyExp(castMethod, args);
  361. return exp;
  362. }
  363. static final Method castableMethod
  364. = typeXDataType.getDeclaredMethod("castable", 1);
  365. public static Expression validateApplyCastableAs
  366. (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
  367. {
  368. exp.visitArgs(visitor);
  369. exp = CompileReflect.inlineClassName(exp, 1, visitor);
  370. Expression[] args = exp.getArgs();
  371. if (args.length != 2 || ! (args[1] instanceof QuoteExp))
  372. return exp;
  373. Object type = ((QuoteExp) args[1]).getValue();
  374. if (type instanceof XDataType)
  375. return new ApplyExp(castableMethod,
  376. new Expression[] { args[1], args[0] });
  377. return exp;
  378. }
  379. }