// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/JavaMemRef.java
// @Slf4j
// public class JavaMemRef extends VmMemRef {
// 
//     private Object obj;
//     private Field field;
// 
//     public JavaMemRef(Object obj, Field field) {
//         this.obj = obj;
//         this.field = field;
//     }
// 
//     @Override
//     public Object read() {
//         try {
//             return field.get(obj);
//         } catch (IllegalArgumentException | IllegalAccessException ex) {
//             log.error("error read " + field.getName() + " of " + obj.getClass().getName(), ex);
//         }
//         return null;
//     }
// 
//     @Override
//     public void write(Object v) {
//         try {
//             field.set(obj, v);
//         } catch (IllegalArgumentException | IllegalAccessException ex) {
//             log.error("error write " + field.getName() + " of " + obj.getClass().getName(), ex);
//         }
//     }
// 
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/JavaMethod.java
// public class JavaMethod implements VmMethod {
// 
//     public Object caller;
//     public ArrayList<Method> methods;
//     String methodName;
// 
//     public JavaMethod(Object caller, ArrayList<Method> methods) {
//         this.caller = caller;
//         this.methods = methods;
//         init();
//     }
// 
//     public JavaMethod(Object caller, Method method) {
//         this.caller = caller;
//         this.methods = new ArrayList<>();
//         this.methods.add(method);
//         init();
//     }
// 
//     void init() {
//         Method m1 = methods.get(0);
//         methodName = m1.getDeclaringClass().getName() + "#" + m1.getName();
//     }
// 
//     @Override
//     public void invoke(VirtualMachine vm, Object[] args) throws Exception {
//         Class[] types = vm.getParamTypes(2);
//         Method properMethod = MethodTypeMap.INSTANCE.getProperMethod(methodName, methods, types);
//         if (properMethod == null) {
//             throw new VmRuntimeException(vm, "no proper method found for " + methods + "[" + Arrays.toString(types) + "]");
//         }
//         if (properMethod.isVarArgs()) {
//             args = VarArgHelper.packVarArgs(args);
//         }
//         try {
//             Object val = properMethod.invoke(caller, args);
//             if (val instanceof Long) {
//                 val = ((Long) val).intValue();
//             }
//             vm.eax.write(val);
//         } catch (Exception ex) {
//             vm.exception.write(new VmRuntimeException(vm, ex.getCause() == null ? ex.toString() : ex.getCause().toString()));
//             Op.restore_machine_state(vm, null, null);
//         }
//     }
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/VmMemRef.java
// public class VmMemRef {
// 
//     private Object value;
//     @Getter
//     private final boolean immutable;
// 
//     public VmMemRef() {
//         this(null, false);
//     }
// 
//     public VmMemRef(Object obj_v) {
//         this(obj_v, false);
//     }
// 
//     public VmMemRef(Object value, boolean immutable) {
//         this.value = value;
//         this.immutable = immutable;
//     }
// 
//     public void write(Object v) {
//         if (immutable && value != null) {
//             throw new RuntimeException("immutable");
//         }
//         this.value = v;
//     }
// 
//     public Object read() {
//         return this.value;
//     }
// 
//     @Override
//     public String toString() {
//         return "VmMemRef{" + "value=" + value + '}';
//     }
// 
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/undefined.java
// public class undefined {
// 
//     private undefined() {
//     }
// 
//     @Override
//     public String toString() {
//         return "undefined";
//     }
// 
//     public static final undefined value = new undefined();
// }


import org.ngscript.runtime.vo.JavaMemRef;
import org.ngscript.runtime.vo.JavaMethod;
import org.ngscript.runtime.vo.VmMemRef;
import org.ngscript.runtime.vo.undefined;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
 * Copyright 2021 wssccc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ngscript.runtime;



/**
 * @author wssccc
 */
public class Environment {

    Map<String, VmMemRef> data = new HashMap<>();
    Environment parent;

    public Environment(Environment parent) {
        this.parent = parent;
    }

    public VmMemRef lookup(String member, VirtualMachine vm, boolean isMember) throws VmRuntimeException {
        //registers
        switch (member) {
            case "this":
                return vm.env;
            case "%eax":
                return vm.eax;
            case "%env":
                return vm.env;
            case "%exception":
                return vm.exception;
            default:
                VmMemRef ref = data.get(member);
                if (ref != null) {
                    return ref;
                } else {
                    ref = lookupHash(member, vm, isMember);
                    data.put(member, ref);
                    return ref;
                }
        }
    }

    private VmMemRef lookupHash(String member, VirtualMachine vm, boolean isMember) throws VmRuntimeException {
        if (isMember) {
            //throw new Runtime-Exception("no member " + varName + " found.");
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/JavaMemRef.java
// @Slf4j
// public class JavaMemRef extends VmMemRef {
// 
//     private Object obj;
//     private Field field;
// 
//     public JavaMemRef(Object obj, Field field) {
//         this.obj = obj;
//         this.field = field;
//     }
// 
//     @Override
//     public Object read() {
//         try {
//             return field.get(obj);
//         } catch (IllegalArgumentException | IllegalAccessException ex) {
//             log.error("error read " + field.getName() + " of " + obj.getClass().getName(), ex);
//         }
//         return null;
//     }
// 
//     @Override
//     public void write(Object v) {
//         try {
//             field.set(obj, v);
//         } catch (IllegalArgumentException | IllegalAccessException ex) {
//             log.error("error write " + field.getName() + " of " + obj.getClass().getName(), ex);
//         }
//     }
// 
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/JavaMethod.java
// public class JavaMethod implements VmMethod {
// 
//     public Object caller;
//     public ArrayList<Method> methods;
//     String methodName;
// 
//     public JavaMethod(Object caller, ArrayList<Method> methods) {
//         this.caller = caller;
//         this.methods = methods;
//         init();
//     }
// 
//     public JavaMethod(Object caller, Method method) {
//         this.caller = caller;
//         this.methods = new ArrayList<>();
//         this.methods.add(method);
//         init();
//     }
// 
//     void init() {
//         Method m1 = methods.get(0);
//         methodName = m1.getDeclaringClass().getName() + "#" + m1.getName();
//     }
// 
//     @Override
//     public void invoke(VirtualMachine vm, Object[] args) throws Exception {
//         Class[] types = vm.getParamTypes(2);
//         Method properMethod = MethodTypeMap.INSTANCE.getProperMethod(methodName, methods, types);
//         if (properMethod == null) {
//             throw new VmRuntimeException(vm, "no proper method found for " + methods + "[" + Arrays.toString(types) + "]");
//         }
//         if (properMethod.isVarArgs()) {
//             args = VarArgHelper.packVarArgs(args);
//         }
//         try {
//             Object val = properMethod.invoke(caller, args);
//             if (val instanceof Long) {
//                 val = ((Long) val).intValue();
//             }
//             vm.eax.write(val);
//         } catch (Exception ex) {
//             vm.exception.write(new VmRuntimeException(vm, ex.getCause() == null ? ex.toString() : ex.getCause().toString()));
//             Op.restore_machine_state(vm, null, null);
//         }
//     }
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/VmMemRef.java
// public class VmMemRef {
// 
//     private Object value;
//     @Getter
//     private final boolean immutable;
// 
//     public VmMemRef() {
//         this(null, false);
//     }
// 
//     public VmMemRef(Object obj_v) {
//         this(obj_v, false);
//     }
// 
//     public VmMemRef(Object value, boolean immutable) {
//         this.value = value;
//         this.immutable = immutable;
//     }
// 
//     public void write(Object v) {
//         if (immutable && value != null) {
//             throw new RuntimeException("immutable");
//         }
//         this.value = v;
//     }
// 
//     public Object read() {
//         return this.value;
//     }
// 
//     @Override
//     public String toString() {
//         return "VmMemRef{" + "value=" + value + '}';
//     }
// 
// }
// 
// Path: ngscript-core/src/main/java/org/ngscript/runtime/vo/undefined.java
// public class undefined {
// 
//     private undefined() {
//     }
// 
//     @Override
//     public String toString() {
//         return "undefined";
//     }
// 
//     public static final undefined value = new undefined();
// }


// Path: ngscript-core/src/main/java/org/ngscript/runtime/Environment.java
import org.ngscript.runtime.vo.JavaMemRef;
import org.ngscript.runtime.vo.JavaMethod;
import org.ngscript.runtime.vo.VmMemRef;
import org.ngscript.runtime.vo.undefined;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/*
 * Copyright 2021 wssccc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ngscript.runtime;



/**
 * @author wssccc
 */
public class Environment {

    Map<String, VmMemRef> data = new HashMap<>();
    Environment parent;

    public Environment(Environment parent) {
        this.parent = parent;
    }

    public VmMemRef lookup(String member, VirtualMachine vm, boolean isMember) throws VmRuntimeException {
        //registers
        switch (member) {
            case "this":
                return vm.env;
            case "%eax":
                return vm.eax;
            case "%env":
                return vm.env;
            case "%exception":
                return vm.exception;
            default:
                VmMemRef ref = data.get(member);
                if (ref != null) {
                    return ref;
                } else {
                    ref = lookupHash(member, vm, isMember);
                    data.put(member, ref);
                    return ref;
                }
        }
    }

    private VmMemRef lookupHash(String member, VirtualMachine vm, boolean isMember) throws VmRuntimeException {
        if (isMember) {
            //throw new Runtime-Exception("no member " + varName + " found.");
            VmMemRef mem = new VmMemRef(undefined.value);