Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

It's impossible to patch a variadic function with function calling an original implementation (ClojureScript only) #3

Open
metametadata opened this issue Jun 22, 2016 · 0 comments

Comments

@metametadata
Copy link
Owner

Steps
See unit.patch tests:

; TODO: fails in CLJS with:
; "TypeError: undefined is not a constructor (evaluating 'unit.fixtures.functions.variadic.cljs$core$IFn$_invoke$arity$1(arguments[0])')"
#?(:clj
   (u/-deftest
     "variadic function can be patched with non-variadic function which calls original function"
     (f/with-fakes
       (f/patch! #'funcs/variadic (fn my-sum
                                    [x]
                                    ((f/original-val #'funcs/variadic) x)))

       (is (= "[a]" (funcs/variadic 100))))))

; TODO: fails in CLJS with "RangeError: Maximum call stack size exceeded."
#?(:clj
   (u/-deftest
     "variadic function can be patched with variadic function which calls original function"
     (f/with-fakes
       (let [original-variadic funcs/variadic]
         (f/patch! #'funcs/variadic (fn my-variadic
                                      ([] (original-variadic))
                                      ([a] ((f/original-val #'funcs/variadic) a))
                                      ([_ _] 2)
                                      ([_ _ _] 3)
                                      ([_ _ _ & _] :etc)))

         (is (= "[]" (funcs/variadic)))
         (is (= "[a]" (funcs/variadic 1)))
         (is (= 2 (funcs/variadic 1 2)))
         (is (= 3 (funcs/variadic 1 2 3)))
         (is (= :etc (funcs/variadic 1 2 3 4 5 6 7)))))))

Cause

Bug appears because of the way variadic functions are compiled to JS code. Example:

(defn variadic
      ([] "[]")
      ([_] "[a]"))

(let [original-variadic variadic
      new-variadic (fn my-variadic
                     []
                     (original-variadic))]
  (set! variadic new-variadic)
  (variadic))

compiles to this JS code:

cljs.user.variadic = (function cljs$user$variadic(var_args){
    ....

    var G__24 = args.length;
    switch (G__24) {
      case (0):
        return cljs.user.variadic.cljs$core$IFn$_invoke$arity$0();
        break;
      case (1):
        return cljs.user.variadic.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));
        break;
      default:
        throw (new Error([cljs.core.str("Invalid arity: "),cljs.core.str(args.length)].join('')));
    }
  });

  cljs.user.variadic.cljs$core$IFn$_invoke$arity$0 = (function (){...});
  cljs.user.variadic.cljs$core$IFn$_invoke$arity$1 = (function (_){...});

  cljs.user.variadic.cljs$lang$maxFixedArity = (1);
  var original_variadic_29 = cljs.user.variadic;
  var new_variadic_30 = ((function (original_variadic_29){
    return (function cljs$user$my_variadic(){
      return original_variadic_29.call(null);
    });
  })(original_variadic_29));

  cljs.user.variadic = new_variadic_30;
  cljs.user.variadic.call(null);

As you can see, cljs.user.variadic = new_variadic_30; will erase cljs.user.variadic.cljs$core$IFn$_invoke$arity$0 leading to a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant