content: package com.alibaba.qlexpress4.runtime;
  
  import com.alibaba.qlexpress4.QLOptions;
  import com.alibaba.qlexpress4.exception.UserDefineException;
  import com.alibaba.qlexpress4.runtime.data.AssignableDataValue;
  import com.alibaba.qlexpress4.runtime.data.convert.ObjTypeConvertor;
  import com.alibaba.qlexpress4.runtime.function.CustomFunction;
  import com.alibaba.qlexpress4.runtime.instruction.QLInstruction;
  import com.alibaba.qlexpress4.runtime.scope.QvmBlockScope;
  
  import java.text.MessageFormat;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Author: DQinYuan
   */
  public class QLambdaInner implements QLambda {
      
      private final QLambdaDefinitionInner lambdaDefinition;
      
      private final QContext qContext;
      
      private final QLOptions qlOptions;
      
      private final boolean newEnv;
      
      public QLambdaInner(QLambdaDefinitionInner lambdaDefinition, QContext qContext, QLOptions qlOptions,
          boolean newEnv) {
          this.lambdaDefinition = lambdaDefinition;
          this.qContext = qContext;
          this.qlOptions = qlOptions;
          this.newEnv = newEnv;
      }
      
      public QResult call(Object... params)
          throws Throwable {
          return callInner(newEnv ? inheritScope(params) : qContext);
      }
      
      @Override
      public Map<String, CustomFunction> getFunctionDefined(Object... params)
          throws Throwable {
          QContext newRuntime = newEnv ? inheritScope(params) : qContext;
          callInner(newRuntime);
          return newRuntime.getFunctionTable();
      }
      
      private QResult callInner(QContext runtime) {
          QLInstruction[] instructions = lambdaDefinition.getInstructions();
          for (int i = 0; i < instructions.length; i++) {
              QResult qResult = instructions[i].execute(runtime, qlOptions);
              switch (qResult.getResultType()) {
                  case JUMP:
                      i += (int)qResult.getResult().get();
                      continue;
                  case RETURN:
                  case BREAK:
                  case CONTINUE:
                      return qResult;
              }
          }
          
          return QResult.NEXT_INSTRUCTION;
      }
      
      private QContext inheritScope(Object[] params)
          throws UserDefineException {
          Map<String, Value> initSymbolTable = new HashMap<>(params.length);
          List<QLambdaDefinitionInner.Param> paramsDefinition = lambdaDefinition.getParamsType();
          for (int i = 0; i < Math.min(params.length, paramsDefinition.size()); i++) {
              QLambdaDefinitionInner.Param paramDefinition = paramsDefinition.get(i);
              Object originParamI = params[i];
              Class<?> targetCls = paramDefinition.getClazz();
              ObjTypeConvertor.QConverted qlConvertResult = ObjTypeConvertor.cast(originParamI, targetCls);
              if (!qlConvertResult.isConvertible()) {
                  throw new UserDefineException(UserDefineException.ExceptionType.INVALID_ARGUMENT,
                      MessageFormat.format(
                          "invalid argument at index {0} (start from 0), required type {1}, but {2} provided",
                          i,
                          targetCls.getName(),
                          originParamI == null ? "null" : originParamI.getClass().getName()));
              }
              initSymbolTable.put(paramDefinition.getName(),
                  new AssignableDataValue(paramDefinition.getName(), qlConvertResult.getConverted(), targetCls));
          }
          // null for rest params
          for (int i = params.length; i < paramsDefinition.size(); i++) {
              QLambdaDefinitionInner.Param paramDefinition = paramsDefinition.get(i);
              initSymbolTable.put(paramDefinition.getName(),
                  new AssignableDataValue(paramDefinition.getName(), null, paramDefinition.getClazz()));
          }
          QvmBlockScope newScope =
              new QvmBlockScope(qContext, initSymbolTable, lambdaDefinition.getMaxStackSize(), ExceptionTable.EMPTY);
          return new DelegateQContext(qContext, newScope);
      }
  }
  