View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript;
16  
17  import static org.htmlunit.BrowserVersionFeatures.JS_NATIVE_FUNCTION_TOSTRING_COMPACT;
18  import static org.htmlunit.BrowserVersionFeatures.JS_NATIVE_FUNCTION_TOSTRING_NL;
19  
20  import org.htmlunit.BrowserVersion;
21  import org.htmlunit.corejs.javascript.BaseFunction;
22  import org.htmlunit.corejs.javascript.Context;
23  import org.htmlunit.corejs.javascript.Function;
24  import org.htmlunit.corejs.javascript.Scriptable;
25  import org.htmlunit.corejs.javascript.ScriptableObject;
26  import org.htmlunit.corejs.javascript.VarScope;
27  
28  /**
29   * Replacement (in fact a wrapper) for Rhino's native toString function on Function prototype
30   * allowing to produce the desired formatting.
31   *
32   * @author Marc Guillemot
33   * @author Ronald Brill
34   * @author Ahmed Ashour
35   */
36  public final class NativeFunctionToStringFunction {
37  
38      /**
39       * Install the wrapper in place of the native toString function on Function's prototype.
40       * @param scope the scope
41       * @param browserVersion the simulated browser
42       */
43      public static void installFix(final VarScope scope, final BrowserVersion browserVersion) {
44          if (browserVersion.hasFeature(JS_NATIVE_FUNCTION_TOSTRING_COMPACT)) {
45              final ScriptableObject fnPrototype =
46                      (ScriptableObject) ScriptableObject.getClassPrototype(scope, "Function");
47              final Function originalToString = (Function) ScriptableObject.getProperty(fnPrototype, "toString");
48              final Function newToString = new NativeFunctionToStringFunctionChrome(originalToString);
49              ScriptableObject.putProperty(fnPrototype, "toString", newToString);
50          }
51          else if (browserVersion.hasFeature(JS_NATIVE_FUNCTION_TOSTRING_NL)) {
52              final ScriptableObject fnPrototype =
53                      (ScriptableObject) ScriptableObject.getClassPrototype(scope, "Function");
54              final Function originalToString = (Function) ScriptableObject.getProperty(fnPrototype, "toString");
55              final Function newToString = new NativeFunctionToStringFunctionFF(originalToString);
56              ScriptableObject.putProperty(fnPrototype, "toString", newToString);
57          }
58      }
59  
60      private NativeFunctionToStringFunction() {
61          super();
62      }
63  
64      static class NativeFunctionToStringFunctionChrome extends FunctionWrapper {
65  
66          NativeFunctionToStringFunctionChrome(final Function wrapped) {
67              super(wrapped);
68          }
69  
70          /**
71           * {@inheritDoc}
72           */
73          @Override
74          public Object call(final Context cx, final VarScope scope, final Scriptable thisObj, final Object[] args) {
75              final String s = (String) super.call(cx, scope, thisObj, args);
76  
77              if (thisObj instanceof BaseFunction function && s.contains("[native code")) {
78                  final String functionName = function.getFunctionName();
79                  return "function " + functionName + "() { [native code] }";
80              }
81              return s.replace("function anonymous() {", "function anonymous(\n) {\n");
82          }
83      }
84  
85      static class NativeFunctionToStringFunctionFF extends FunctionWrapper {
86  
87          NativeFunctionToStringFunctionFF(final Function wrapped) {
88              super(wrapped);
89          }
90  
91          /**
92           * {@inheritDoc}
93           */
94          @Override
95          public Object call(final Context cx, final VarScope scope, final Scriptable thisObj, final Object[] args) {
96              final String s = (String) super.call(cx, scope, thisObj, args);
97  
98              if (thisObj instanceof BaseFunction function && s.contains("[native code")) {
99                  final String functionName = function.getFunctionName();
100                 return "function " + functionName + "() {\n    [native code]\n}";
101             }
102             return s.replace("function anonymous() {", "function anonymous(\n) {\n");
103         }
104     }
105 }