gt:       if (arglen == null)
context: /*
   * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
   *
   * This file is part of Resin(R) Open Source
   *
   * Each copy or derived work must preserve the copyright notice and this
   * notice unmodified.
   *
   * Resin Open Source is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * Resin Open Source is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
   * of NON-INFRINGEMENT.  See the GNU General Public License for more
   * details.
   *
   * You should have received a copy of the GNU General Public License
   * along with Resin Open Source; if not, write to the
   *   Free SoftwareFoundation, Inc.
   *   59 Temple Place, Suite 330
   *   Boston, MA 02111-1307  USA
   *
   * @author Scott Ferguson
   */
  
  package com.caucho.es;
  
  import com.caucho.es.parser.Parser;
  import com.caucho.vfs.ReadStream;
  import com.caucho.vfs.Vfs;
  
  import java.io.IOException;
  import java.util.ArrayList;
  
  /**
   * JavaScript object
   */
  class NativeFunction extends Native {
    static final int NEW = 1;
    static final int TO_STRING = NEW + 1;
    static final int CALL = TO_STRING + 1;
    static final int APPLY = CALL + 1;
  
    static ESId LENGTH = ESId.intern("length");
  
    /**
     * Create a new object based on a prototype
     */
    private NativeFunction(String name, int n, int len)
    {
      super(name, len);
  
      this.n = n;
    }
  
    /**
     * Creates the native Function object
     *
     * XXX: incomplete
     */
    static NativeWrapper create(Global resin)
    {
      ESBase []scope = new ESBase[] { resin.getGlobalProto() };
      ESClosure funProto = new ESClosure(scope, 1);
      funProto.name = ESId.intern("Function");
  
      Native natFunction = new NativeFunction("Function", NEW, 1);
      NativeWrapper function = new NativeWrapper(resin, natFunction,
                                                 funProto, ESThunk.FUN_THUNK);
      resin.funProto = funProto;
      
      put(funProto, "toString", TO_STRING, 0);
      put(funProto, "call", CALL, 1);
      put(funProto, "apply", APPLY, 2);
      funProto.prototype = resin.objProto;
  
      funProto.setClean();
      function.setClean();
  
      return function;
    }
  
    private static void put(ESObject proto, String name, int n, int len)
    {
      ESId id = ESId.intern(name);
  
      proto.put(id, new NativeFunction(name, n, len), DONT_ENUM);
    }
  
    public ESBase call(Call eval, int length) throws Throwable
    {
      switch (n) {
      case NEW:
        return createAnonymous(eval, length);
  
        // Object prototype stuff
      case TO_STRING:
        // XXX: Is this correct?  Test.
        if (eval.getThis() instanceof ESClosure) {
          ESClosure closure = (ESClosure) eval.getThis();
  
          return ESString.create(closure.decompile());
        } else if (eval.getThis() instanceof NativeWrapper) {
          NativeWrapper wrapper = (NativeWrapper) eval.getThis();
  
          return wrapper.fun.toStr();
        } else
        throw new ESException("to string bound to function: " +
                            eval.getThis().getClass());
  
      case CALL:
        int oldTop = eval.top;
        ESBase fun = eval.getArg(-1);
        ESBase callThis = null;
  
        try {
          if (length > 0) {
            callThis = eval.getArg(0);
          } else
            callThis = esNull;
  
          if (callThis == esNull || callThis == esUndefined ||
              callThis == esEmpty)
            eval.setArg(0, eval.getGlobal());
          else
            eval.setArg(0, callThis.toObject());
          eval.top++;
  
          return fun.call(eval, length > 0 ? length - 1 : 0);
        } finally {
          eval.top = oldTop;
        }
  
      case APPLY:
        return apply(eval, length);
  
      default:
        throw new ESException("Unknown object function");
      }
    }
  
    /**
     * Create a new parser instance.
     */
    private ESClosure parseFunction(Call eval, int length) throws Throwable
    {
      StringBuffer sbuf = new StringBuffer();
  
      sbuf.append("function anonymous(");
      ArrayList argList = new ArrayList();
      for (int i = 0; i < length - 1; i++) {
        if (i != 0)
          sbuf.append(",");
        String str = eval.getArg(i).toString();
        int j = 0;
        int p = 0;
        
        while ((p = str.indexOf(',', j)) >= 0 ||
               (p = str.indexOf(' ', j)) >= 0) {
          if (j < p)
            argList.add(ESId.intern(str.substring(j, p)));
          j = p + 1;
        }
        if (j < str.length())
          argList.add(ESId.intern(str.substring(j)));
        
        sbuf.append(str);
      }
      ESId []args = new ESId[argList.size()];
      argList.toArray(args);
      sbuf.append("){");
      if (length > 0)
        sbuf.append(eval.getArg(length - 1).toString());
      sbuf.append("}\n");
      sbuf.append("return anonymous();");
  
      Global resin = Global.getGlobalProto();
      Script script = null;
      try {
        Parser parser = new Parser();
        ReadStream is = Vfs.openString(sbuf.toString());
        script = parser.parse(is, "anonymous", 1);
        is.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
  
      ESCallable jsClass = script.initClass(resin, eval.getGlobal());
  
      // It's known that the function will be #2.
      ESClosure fun = new ESClosure(ESId.intern("anonymous"), jsClass,
                                    null, 2, args, eval.getGlobal());
      
      return fun;
    }
  
    private ESBase createAnonymous(Call eval, int length) throws Throwable
    {
      return parseFunction(eval, length);
    }
  
    private ESBase apply(Call eval, int length) throws Throwable
    {
      Global resin = Global.getGlobalProto();
      Call call = eval.getCall();
  
      call.top = 1;
      call.global = eval.global;
      call.caller = eval;
  
      ESBase fun = eval.getArg(-1);
      ESBase callThis = null;
  
      if (length > 0) {
        callThis = eval.getArg(0);
      } else
        callThis = esNull;
  
      if (callThis == esNull || callThis == esUndefined ||
          callThis == esEmpty)
        call.setArg(-1, eval.getGlobal());
      else
        call.setArg(-1, callThis.toObject());
  
      int j = 0;
      for (int i = 1; i < length; i++) {
        ESBase arg = eval.getArg(i);
  
        if (arg == esNull || arg == esUndefined || arg == esEmpty)
          continue;
  
        ESBase arglen = arg.hasProperty(LENGTH);
  
  