1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.jexl3; 19 20 import java.nio.charset.Charset; 21 import java.util.Arrays; 22 import java.util.Collection; 23 import java.util.Map; 24 import java.util.function.IntFunction; 25 import java.util.function.Supplier; 26 27 import org.apache.commons.jexl3.internal.Engine; 28 import org.apache.commons.jexl3.internal.SoftCache; 29 import org.apache.commons.jexl3.introspection.JexlPermissions; 30 import org.apache.commons.jexl3.introspection.JexlSandbox; 31 import org.apache.commons.jexl3.introspection.JexlUberspect; 32 import org.apache.commons.jexl3.parser.JexlScriptParser; 33 import org.apache.commons.logging.Log; 34 35 /** 36 * Configures and builds a JexlEngine. 37 * 38 * <p> 39 * The builder allow fine-tuning an engine instance behavior according to various control needs. 40 * Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>. 41 * </p><p> 42 * Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL 43 * syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control 44 * the visible set of objects - for instance, avoiding access to any object in java.rmi.* -. 45 * </p><p> 46 * Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most 47 * common flags accessible from the builder are reflected in its options ({@link #options()}). 48 * </p><p> 49 * The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as 50 * warning, when false, they throw {@link JexlException} exceptions. 51 * </p><p> 52 * The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe} 53 * flog determines if safe-navigation is used. Safe-navigation allows an evaluation shortcut and return null in expressions 54 * that attempts dereferencing null, typically a method call or accessing a property. 55 * </p><p> 56 * The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for 57 * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be 58 * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be 59 * preferred since they will detect violations at parsing time. (see {@link JexlFeatures}) 60 * </p><p> 61 * The following rules apply on silent and strict flags: 62 * </p> 63 * <ul> 64 * <li>When "silent" & "not-strict": 65 * <p> 0 & null should be indicators of "default" values so that even in an case of error, 66 * something meaningful can still be inferred; may be convenient for configurations. 67 * </p> 68 * </li> 69 * <li>When "silent" & "strict": 70 * <p>One should probably consider using null as an error case - ie, every object 71 * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form 72 * can be used to workaround exceptional cases. 73 * Use case could be configuration with no implicit values or defaults. 74 * </p> 75 * </li> 76 * <li>When "not-silent" & "not-strict": 77 * <p>The error control grain is roughly on par with JEXL 1.0</p> 78 * </li> 79 * <li>When "not-silent" & "strict": 80 * <p>The finest error control grain is obtained; it is the closest to Java code - 81 * still augmented by "script" capabilities regarding automated conversions and type matching. 82 * </p> 83 * </li> 84 * </ul> 85 */ 86 public class JexlBuilder { 87 /** 88 * The set of default permissions used when creating a new builder. 89 * <p>Static but modifiable so these default permissions can be changed to a purposeful set.</p> 90 * <p>In JEXL 3.3, these are {@link JexlPermissions#RESTRICTED}.</p> 91 * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p> 92 */ 93 private static JexlPermissions PERMISSIONS = JexlPermissions.RESTRICTED; 94 95 /** The default maximum expression length to hit the expression cache. */ 96 protected static final int CACHE_THRESHOLD = 64; 97 98 /** 99 * Sets the default permissions. 100 * @param permissions the permissions 101 */ 102 public static void setDefaultPermissions(final JexlPermissions permissions) { 103 PERMISSIONS = permissions == null ? JexlPermissions.RESTRICTED : permissions; 104 } 105 106 /** The JexlUberspect instance. */ 107 private JexlUberspect uberspect; 108 109 /** The {@link JexlUberspect} resolver strategy. */ 110 private JexlUberspect.ResolverStrategy strategy; 111 112 /** The set of permissions. */ 113 private JexlPermissions permissions; 114 115 /** The sandbox. */ 116 private JexlSandbox sandbox; 117 118 /** The Log to which all JexlEngine messages will be logged. */ 119 private Log logger; 120 121 /** Whether error messages will carry debugging information. */ 122 private Boolean debug; 123 124 /** Whether interrupt throws JexlException.Cancel. */ 125 private Boolean cancellable; 126 127 /** The options. */ 128 private final JexlOptions options = new JexlOptions(); 129 130 /** Whether getVariables considers all potential equivalent syntactic forms. */ 131 private int collectMode = 1; 132 133 /** The {@link JexlArithmetic} instance. */ 134 private JexlArithmetic arithmetic; 135 136 /** The cache size. */ 137 private int cache = -1; 138 139 /** The cache class factory. */ 140 private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new; 141 142 /** The parser class factory. */ 143 private Supplier<JexlScriptParser> parserFactory; 144 145 /** The stack overflow limit. */ 146 private int stackOverflow = Integer.MAX_VALUE; 147 148 /** The maximum expression length to hit the expression cache. */ 149 private int cacheThreshold = CACHE_THRESHOLD; 150 151 /** The charset. */ 152 private Charset charset = Charset.defaultCharset(); 153 154 /** The class loader. */ 155 private ClassLoader loader; 156 157 /** The features. */ 158 private JexlFeatures features; 159 160 /** 161 * Default constructor. 162 * <p> 163 * As of JEXL 3.3, to reduce the security risks inherent to JEXL"s purpose, the builder will use a set of 164 * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes 165 * and methods are visible to JEXL and usable in scripts using default implicit behaviors. 166 * </p><p> 167 * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing 168 * your own class instances through arguments, contexts or namespaces. 169 * The new default set of allowed packages and denied classes is described by {@link JexlPermissions#RESTRICTED}. 170 * </p><p> 171 * The recommended mitigation if your usage of JEXL is impacted is to first thoroughly review what should be 172 * allowed and exposed to script authors and implement those through a set of {@link JexlPermissions}; 173 * those are easily created using {@link JexlPermissions#parse(String...)}. 174 * </p><p> 175 * In the urgent case of a strict 3.2 compatibility, the simplest and fastest mitigation is to use the 'unrestricted' 176 * set of permissions. The builder must be explicit about it either by setting the default permissions with a 177 * statement like {@code JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);} or with a more precise 178 * one like <code>new JexlBuilder().permissions({@link JexlPermissions#UNRESTRICTED})</code>. 179 * </p><p> 180 * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior 181 * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}. 182 * </p> 183 * @since 3.3 184 */ 185 public JexlBuilder() { 186 this.permissions = PERMISSIONS; 187 } 188 189 /** 190 * Is antish resolution enabled? 191 * @return whether antish resolution is enabled 192 */ 193 public boolean antish() { 194 return options.isAntish(); 195 } 196 197 /** 198 * Sets whether the engine will resolve antish variable names. 199 * 200 * @param flag true means antish resolution is enabled, false disables it 201 * @return this builder 202 */ 203 public JexlBuilder antish(final boolean flag) { 204 options.setAntish(flag); 205 return this; 206 } 207 208 /** 209 * Gets the JexlArithmetic instance the engine will use. 210 * @return the arithmetic 211 */ 212 public JexlArithmetic arithmetic() { 213 return this.arithmetic; 214 } 215 216 /** 217 * Sets the JexlArithmetic instance the engine will use. 218 * 219 * @param a the arithmetic 220 * @return this builder 221 */ 222 public JexlBuilder arithmetic(final JexlArithmetic a) { 223 this.arithmetic = a; 224 options.setStrictArithmetic(a.isStrict()); 225 options.setMathContext(a.getMathContext()); 226 options.setMathScale(a.getMathScale()); 227 return this; 228 } 229 230 /** 231 * Sets whether logical expressions ("" , ||) coerce their result to boolean. 232 * @param flag true or false 233 * @return this builder 234 */ 235 public JexlBuilder booleanLogical(final boolean flag) { 236 options.setBooleanLogical(flag); 237 return this; 238 } 239 240 /** 241 * Gets the expression cache size the engine will use. 242 * @return the cache size 243 */ 244 public int cache() { 245 return cache; 246 } 247 248 /** 249 * Sets the expression cache size the engine will use. 250 * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length. 251 * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p> 252 * 253 * @param size if not strictly positive, no cache is used. 254 * @return this builder 255 */ 256 public JexlBuilder cache(final int size) { 257 this.cache = size; 258 return this; 259 } 260 261 /** 262 * Gets the expression-cache factory the engine will use. 263 * @return the cache factory 264 */ 265 public IntFunction<JexlCache<?, ?>> cacheFactory() { 266 return this.cacheFactory; 267 } 268 269 /** 270 * Sets the expression-cache factory the engine will use. 271 * 272 * @param factory the function to produce a cache. 273 * @return this builder 274 */ 275 public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) { 276 this.cacheFactory = factory; 277 return this; 278 } 279 280 /** 281 * Gets the Jexl script parser factory the engine will use. 282 * @return the cache factory 283 * @since 3.5.0 284 */ 285 public Supplier<JexlScriptParser> parserFactory() { 286 return this.parserFactory; 287 } 288 289 /** 290 * Sets the Jexl script parser factory the engine will use. 291 * 292 * @param factory the function to produce a cache. 293 * @return this builder 294 * @since 3.5.0 295 */ 296 public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) { 297 this.parserFactory = factory; 298 return this; 299 } 300 301 /** 302 * Gets the maximum length for an expression to be cached. 303 * @return the cache threshold 304 */ 305 public int cacheThreshold() { 306 return cacheThreshold; 307 } 308 309 /** 310 * Sets the maximum length for an expression to be cached. 311 * <p>Expression whose length is greater than this expression cache length threshold will 312 * bypass the cache.</p> 313 * <p>It is expected that a "long" script will be parsed once and its reference kept 314 * around in user-space structures; the jexl expression cache has no added-value in this case.</p> 315 * 316 * @param length if not strictly positive, the value is silently replaced by the default value (64). 317 * @return this builder 318 */ 319 public JexlBuilder cacheThreshold(final int length) { 320 this.cacheThreshold = length > 0? length : CACHE_THRESHOLD; 321 return this; 322 } 323 324 /** 325 * Gets the cancellable information flag 326 * @return the cancellable information flag 327 * @since 3.1 328 */ 329 public Boolean cancellable() { 330 return this.cancellable; 331 } 332 333 /** 334 * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation 335 * and return null. 336 * 337 * @param flag true implies the engine throws the exception, false makes the engine return null. 338 * @return this builder 339 * @since 3.1 340 */ 341 public JexlBuilder cancellable(final boolean flag) { 342 this.cancellable = flag; 343 options.setCancellable(flag); 344 return this; 345 } 346 347 /** 348 * Gets the charset 349 * @return the charset 350 */ 351 public Charset charset() { 352 return charset; 353 } 354 355 /** 356 * Sets the charset to use. 357 * 358 * @param arg the charset 359 * @return this builder 360 * @since 3.1 361 */ 362 public JexlBuilder charset(final Charset arg) { 363 this.charset = arg; 364 return this; 365 } 366 367 /** 368 * Does the variable collection follow strict syntactic rule? 369 * @return true if variable collection follows strict syntactic rule 370 * @since 3.2 371 */ 372 public boolean collectAll() { 373 return this.collectMode != 0; 374 } 375 376 /** 377 * Sets whether the engine variable collectors considers all potential forms of variable syntaxes. 378 * 379 * @param flag true means var collections considers constant array accesses equivalent to dotted references 380 * @return this builder 381 * @since 3.2 382 */ 383 public JexlBuilder collectAll(final boolean flag) { 384 return collectMode(flag? 1 : 0); 385 } 386 387 /** 388 * Gets the collection mode. 389 * @return 0 if variable collection follows strict syntactic rule 390 * @since 3.2 391 */ 392 public int collectMode() { 393 return this.collectMode; 394 } 395 396 /** 397 * Experimental collector mode setter. 398 * 399 * @param mode 0 or 1 as equivalents to false and true, other values are experimental 400 * @return this builder 401 * @since 3.2 402 */ 403 public JexlBuilder collectMode(final int mode) { 404 this.collectMode = mode; 405 return this; 406 } 407 408 /** 409 * Create a new engine 410 * @return a {@link JexlEngine} instance 411 */ 412 public JexlEngine create() { 413 return new Engine(this); 414 } 415 416 /** 417 * Gets the debug flag 418 * @return the debugging information flag 419 */ 420 public Boolean debug() { 421 return this.debug; 422 } 423 424 /** 425 * Sets whether the engine will report debugging information when error occurs. 426 * 427 * @param flag true implies debug is on, false implies debug is off. 428 * @return this builder 429 */ 430 public JexlBuilder debug(final boolean flag) { 431 this.debug = flag; 432 return this; 433 } 434 435 /** 436 * Gets the features the engine will use as a base by default. 437 * @return the features 438 */ 439 public JexlFeatures features() { 440 return this.features; 441 } 442 443 /** 444 * Sets the features the engine will use as a base by default. 445 * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts. 446 * <p>Note also that these will apply to template expressions and scripts. 447 * <p>As a last remark, if lexical or lexicalShade are set as features, this 448 * method will also set the corresponding options. 449 * @param f the features 450 * @return this builder 451 */ 452 public JexlBuilder features(final JexlFeatures f) { 453 this.features = f; 454 if (features != null) { 455 if (features.isLexical()) { 456 options.setLexical(true); 457 } 458 if (features.isLexicalShade()) { 459 options.setLexicalShade(true); 460 } 461 } 462 return this; 463 } 464 465 /** 466 * Gets the optional set of imported packages. 467 * @return the set of imports, may be empty, not null 468 */ 469 public Collection<String> imports() { 470 return options.getImports(); 471 } 472 473 /** 474 * Sets the optional set of imports. 475 * @param imports the imported packages 476 * @return this builder 477 */ 478 public JexlBuilder imports(final Collection<String> imports) { 479 options.setImports(imports); 480 return this; 481 } 482 483 /** 484 * Sets the optional set of imports. 485 * @param imports the imported packages 486 * @return this builder 487 */ 488 public JexlBuilder imports(final String... imports) { 489 return imports(Arrays.asList(imports)); 490 } 491 492 /** 493 * Is lexical scope enabled? 494 * @see JexlOptions#isLexical() 495 * @return whether lexical scope is enabled 496 * @deprecated 3.5.0 497 */ 498 @Deprecated 499 public boolean lexical() { 500 return options.isLexical(); 501 } 502 503 /** 504 * Sets whether the engine is in lexical mode. 505 * 506 * @param flag true means lexical function scope is in effect, false implies non-lexical scoping 507 * @return this builder 508 * @since 3.2 509 */ 510 public JexlBuilder lexical(final boolean flag) { 511 options.setLexical(flag); 512 return this; 513 } 514 515 /** 516 * Checks whether lexical shading is enabled. 517 * @see JexlOptions#isLexicalShade() 518 * @return whether lexical shading is enabled 519 * @deprecated 3.5.0 520 */ 521 @Deprecated 522 public boolean lexicalShade() { 523 return options.isLexicalShade(); 524 } 525 526 /** 527 * Sets whether the engine is in lexical shading mode. 528 * 529 * @param flag true means lexical shading is in effect, false implies no lexical shading 530 * @return this builder 531 * @since 3.2 532 */ 533 public JexlBuilder lexicalShade(final boolean flag) { 534 options.setLexicalShade(flag); 535 return this; 536 } 537 538 /** 539 * Gets the classloader 540 * @return the class loader 541 */ 542 public ClassLoader loader() { 543 return loader; 544 } 545 546 /** 547 * Sets the charset to use. 548 * 549 * @param arg the charset 550 * @return this builder 551 * @deprecated since 3.1 use {@link #charset(Charset)} instead 552 */ 553 @Deprecated 554 public JexlBuilder loader(final Charset arg) { 555 return charset(arg); 556 } 557 558 /** 559 * Sets the class loader to use. 560 * 561 * @param l the class loader 562 * @return this builder 563 */ 564 public JexlBuilder loader(final ClassLoader l) { 565 this.loader = l; 566 return this; 567 } 568 569 /** 570 * Gets the logger 571 * @return the logger 572 */ 573 public Log logger() { 574 return this.logger; 575 } 576 577 /** 578 * Sets the o.a.c.Log instance to use. 579 * 580 * @param log the logger 581 * @return this builder 582 */ 583 public JexlBuilder logger(final Log log) { 584 this.logger = log; 585 return this; 586 } 587 588 /** 589 * Gets the map of namespaces. 590 * @return the map of namespaces. 591 */ 592 public Map<String, Object> namespaces() { 593 return options.getNamespaces(); 594 } 595 596 /** 597 * Sets the default namespaces map the engine will use. 598 * <p> 599 * Each entry key is used as a prefix, each entry value used as a bean implementing 600 * methods; an expression like 'nsx:method(123)' will thus be solved by looking at 601 * a registered bean named 'nsx' that implements method 'method' in that map. 602 * If all methods are static, you may use the bean class instead of an instance as value. 603 * </p> 604 * <p> 605 * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance 606 * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext 607 * to carry the information used by the namespace to avoid variable space pollution and strongly type 608 * the constructor with this specialized JexlContext. 609 * </p> 610 * <p> 611 * The key or prefix allows to retrieve the bean that plays the role of the namespace. 612 * If the prefix is null, the namespace is the top-level namespace allowing to define 613 * top-level user-defined namespaces ( ie: myfunc(...) ) 614 * </p> 615 * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext 616 * derived instances to call methods on the wrapped object.</p> 617 * 618 * @param ns the map of namespaces 619 * @return this builder 620 */ 621 public JexlBuilder namespaces(final Map<String, Object> ns) { 622 options.setNamespaces(ns); 623 return this; 624 } 625 626 /** 627 * Gets the current set of options 628 * @return the current set of options 629 */ 630 public JexlOptions options() { 631 return options; 632 } 633 634 /** 635 * Gets the permissions 636 * @return the permissions 637 */ 638 public JexlPermissions permissions() { 639 return this.permissions; 640 } 641 642 /** 643 * Sets the JexlPermissions instance the engine will use. 644 * 645 * @param p the permissions 646 * @return this builder 647 */ 648 public JexlBuilder permissions(final JexlPermissions p) { 649 this.permissions = p; 650 return this; 651 } 652 653 /** 654 * Is it safe to dereference null? 655 * @return true if safe, false otherwise 656 */ 657 public Boolean safe() { 658 return options.isSafe(); 659 } 660 661 /** 662 * Sets whether the engine considers dereferencing null in navigation expressions 663 * as null or triggers an error. 664 * <p>{@code x.y()} if x is null throws an exception when not safe, 665 * return null and warns if it is.</p> 666 * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p> 667 * 668 * @param flag true means safe navigation, false throws exception when dereferencing null 669 * @return this builder 670 */ 671 public JexlBuilder safe(final boolean flag) { 672 options.setSafe(flag); 673 return this; 674 } 675 676 /** 677 * Gets the sandbox 678 * @return the sandbox 679 */ 680 public JexlSandbox sandbox() { 681 return this.sandbox; 682 } 683 684 /** 685 * Sets the sandbox the engine will use. 686 * 687 * @param box the sandbox 688 * @return this builder 689 */ 690 public JexlBuilder sandbox(final JexlSandbox box) { 691 this.sandbox = box; 692 return this; 693 } 694 695 /** 696 * Is error handling silent? 697 * @return the silent error handling flag 698 */ 699 public Boolean silent() { 700 return options.isSilent(); 701 } 702 703 /** 704 * Sets whether the engine will throw JexlException during evaluation when an error is triggered. 705 * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an 706 * error.</p> 707 * <p>It is recommended to use <em>silent(true)</em> as an explicit default.</p> 708 * @param flag true means no JexlException will occur, false allows them 709 * @return this builder 710 */ 711 public JexlBuilder silent(final boolean flag) { 712 options.setSilent(flag); 713 return this; 714 } 715 716 /** 717 * Gets the cache size 718 * @return the cache size 719 */ 720 public int stackOverflow() { 721 return stackOverflow; 722 } 723 724 /** 725 * Sets the number of script/expression evaluations that can be stacked. 726 * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown. 727 * @return this builder 728 */ 729 public JexlBuilder stackOverflow(final int size) { 730 this.stackOverflow = size; 731 return this; 732 } 733 734 /** 735 * Gets the JexlUberspect strategy 736 * @return the JexlUberspect strategy */ 737 public JexlUberspect.ResolverStrategy strategy() { 738 return this.strategy; 739 } 740 741 /** 742 * Sets the JexlUberspect strategy the engine will use. 743 * <p>This is ignored if the uberspect has been set. 744 * 745 * @param rs the strategy 746 * @return this builder 747 */ 748 public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) { 749 this.strategy = rs; 750 return this; 751 } 752 753 /** 754 * Is it strict mode? 755 * @return true if strict, false otherwise */ 756 public Boolean strict() { 757 return options.isStrict(); 758 } 759 760 /** 761 * Sets whether the engine considers unknown variables, methods, functions and constructors as errors or 762 * evaluates them as null. 763 * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When 764 * strict, those raise exceptions.</p> 765 * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p> 766 * 767 * @param flag true means strict error reporting, false allows them to be evaluated as null 768 * @return this builder 769 */ 770 public JexlBuilder strict(final boolean flag) { 771 options.setStrict(flag); 772 return this; 773 } 774 775 /** 776 * Is interpolation strict? 777 * @see JexlOptions#setStrictInterpolation(boolean) 778 * @param flag strict interpolation flag 779 * @return this builder 780 */ 781 public JexlBuilder strictInterpolation(final boolean flag) { 782 options.setStrictInterpolation(flag); 783 return this; 784 } 785 786 /** 787 * Gets the uberspect 788 * @return the uberspect */ 789 public JexlUberspect uberspect() { 790 return this.uberspect; 791 } 792 793 /** 794 * Sets the JexlUberspect instance the engine will use. 795 * 796 * @param u the uberspect 797 * @return this builder 798 */ 799 public JexlBuilder uberspect(final JexlUberspect u) { 800 this.uberspect = u; 801 return this; 802 } 803 }