Package erjang

Source Code of erjang.ETuple

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* 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 erjang;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;

import erjang.driver.IO;
import erjang.m.ets.EMatchContext;
import erjang.m.ets.EPattern;
import erjang.m.ets.ETermPattern;

public abstract class ETuple extends EObject implements Cloneable /* , Indexed */{

  @Override
  int cmp_order() {
    return CMP_ORDER_TUPLE;
  }

  @Override
  int compare_same(EObject rhs) {
    ETuple other = (ETuple) rhs;
   
    // TODO: is this the correct ordering? .. it seems so:
    //
    // 1> {1,2} < {3}.
    // false
    // 2> {2} < {1,1}.
    // true
    // 3> {2,2} < {1,1}.
    // false
    // 4> {1,1} > {2,2}.
    // false
    // 5> {1,1} < {2,2}.
    // true

    if (arity() < other.arity())
      return -1;
    if (arity() > other.arity())
      return 1;

    for (int i = 1; i <= arity(); i++) {
      int cmp = elm(i).erlangCompareTo(other.elm(i));
      if (cmp != 0)
        return cmp;
    }

    return 0;
  }

  public ETuple testTuple() {
    return this;
  }
 
  public boolean match(ETermPattern matcher, EMatchContext r) {
    return matcher.match(this, r);
  }

  @Override
  public ETermPattern compileMatch(Set<Integer> out) {
    return EPattern.compilePattern(this, out);
  }



  public abstract int arity();

  public abstract EObject elm(int i);

  public static ETuple make(int len) {
    switch (len) {
    case 0:
      return new ETuple0();
    case 1:
      return new ETuple1();
    case 2:
      return new ETuple2();
    case 3:
      return new ETuple3();
    case 4:
      return new ETuple4();
    default:
      return make_big(len);
    }
  }

  public static ETuple make(EObject... array) {
    ETuple res = make(array.length);
    for (int i = 0; i < array.length; i++) {
      res.set(i + 1, array[i]);
    }
    return res;
  }

  @Override
  public ETuple clone() {
    try {
      return (ETuple) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new Error();
    }
  }

  /** Basis for erlang:setelement - clone this and set element.
   *
   * Equivalent to
   *
   * <pre> ETuple res = x.clone();
   * res.set(index, term);</pre>
   * TODO: it may make sense (for performance) to code generate this
   * specifically for each subclass.  Lets do that when it pops
   * up in a profiler one day.
   *
   * @param index 1-based index to set
   * @param term value to put at index
   * @return copy of this, with index position set to term.
   */
  public ETuple setelement(int index, EObject term) {
    ETuple t = clone();
    t.set(index, term);
    return t;
  }

  public abstract void set(int index, EObject term);

  public abstract ETuple blank();

  private static final Type EOBJECT_TYPE = Type.getType(EObject.class);
  private static final String EOBJECT_DESC = EOBJECT_TYPE.getDescriptor();
  private static final Type ETUPLE_TYPE = Type.getType(ETuple.class);
  private static final String ETUPLE_NAME = ETUPLE_TYPE.getInternalName();
  private static final Type ETERM_TYPE = Type.getType(EObject.class);

  private static Map<Integer, ETuple> protos = new HashMap<Integer, ETuple>();

  @SuppressWarnings("unchecked")
  private static ETuple make_big(int size) {

    ETuple proto = protos.get(size);

    if (proto == null) {
      try {
        Class<? extends ETuple> c = get_tuple_class(size);
        protos.put(size, proto = c.newInstance());
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    return proto.blank();
  }

  /*
   * static class XClassLoader extends ClassLoader { public XClassLoader() {
   * super(ETuple.class.getClassLoader()); }
   *
   * Class<?> define(String name, byte[] b) { return super.defineClass(name,
   * b, 0, b.length); } }
   *
   * static XClassLoader loader2 = new XClassLoader();
   */

  @SuppressWarnings("unchecked")
  static public Class<? extends ETuple> get_tuple_class(int num_cells) {

    try {
      return (Class<? extends ETuple>) Class.forName(ETuple.class.getName() + num_cells);
    } catch (ClassNotFoundException e) {
      // make it!
    }

    byte[] data = make_tuple_class_data(num_cells);

    //dump(ETUPLE_NAME + num_cells, data);

    String name = (ETUPLE_NAME + num_cells).replace('/', '.');

        synchronized (ETuple.class) { // Avoid race conditions
            try {
                // Has the class been defined in the meantime?
                return (Class<? extends ETuple>) Class.forName(ETuple.class.getName() + num_cells);
            } catch (ClassNotFoundException e) {
                // It is still not defined - do it
                return ERT.defineClass(ETuple.class.getClassLoader(), name, data);
            }
        }
  }

  static byte[] make_tuple_class_data(int num_cells) {
    ClassWriter cww = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);

    CheckClassAdapter cw = new CheckClassAdapter(cww);

    String this_class_name = ETUPLE_NAME + num_cells;
    String super_class_name = ETUPLE_NAME;
    cw.visit(Opcodes.V1_4, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
        this_class_name, null, super_class_name, null);

    // create fields

    for (int i = 1; i <= num_cells; i++) {
      cw.visitField(Opcodes.ACC_PUBLIC, "elem" + i, ETERM_TYPE
          .getDescriptor(), null, null);
    }

    // create count method
    create_count(cw, num_cells);

    // create cast method
    create_cast(cw, num_cells);
    create_cast2(cw, num_cells);

    // create constructor
    create_constructor(cw, super_class_name);

    // create copy
    create_tuple_copy(num_cells, cw, this_class_name, super_class_name);

    create_tuple_make(num_cells, cw, this_class_name, super_class_name);
    create_tuple_make2(num_cells, cw, this_class_name, super_class_name);
    // create nth
    create_tuple_nth(num_cells, cw, this_class_name);

    // create set
    create_tuple_set(num_cells, cw, this_class_name);

    cw.visitEnd();
    byte[] data = cww.toByteArray();

    // dump(this_class_name, data);

    return data;
  }

  private static void create_tuple_copy(int i, ClassVisitor cw,
      String this_class_name, String super_class_name) {
    MethodVisitor mv;
    make_blank_bridge(cw, this_class_name, super_class_name);

    mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "blank", "()L"
        + this_class_name + ";", null, null);
    mv.visitCode();
    mv.visitTypeInsn(Opcodes.NEW, this_class_name);
    mv.visitInsn(Opcodes.DUP);
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, this_class_name, "<init>",
        "()V");

    mv.visitInsn(Opcodes.ARETURN);

    mv.visitMaxs(3, 3);
    mv.visitEnd();
  }

  private static void create_tuple_make(int i, ClassVisitor cw,
      String this_class_name, String super_class_name) {
    MethodVisitor mv;

    mv = cw.visitMethod(Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC, "create", "()L"
        + this_class_name + ";", null, null);
    mv.visitCode();
    mv.visitTypeInsn(Opcodes.NEW, this_class_name);
    mv.visitInsn(Opcodes.DUP);
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, this_class_name, "<init>",
        "()V");

    mv.visitInsn(Opcodes.ARETURN);

    mv.visitMaxs(3, 3);
    mv.visitEnd();
  }

  private static void create_tuple_make2(int arity, ClassVisitor cw,
      String this_class_name, String super_class_name) {
    MethodVisitor mv;

    StringBuffer sb = new StringBuffer("(");
    for (int i = 0; i<arity; i++) sb.append(EOBJECT_DESC);
    sb.append(")L");
    sb.append(this_class_name);
    sb.append(";");
   
    mv = cw.visitMethod(Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC, "make_tuple", sb.toString(), null, null);

    mv.visitCode();
    mv.visitTypeInsn(Opcodes.NEW, this_class_name);
    mv.visitInsn(Opcodes.DUP);
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, this_class_name, "<init>",
        "()V");

    for (int i = 0; i < arity; i++) {
      mv.visitInsn(Opcodes.DUP);
      mv.visitVarInsn(Opcodes.ALOAD, i);
      mv.visitFieldInsn(Opcodes.PUTFIELD, this_class_name, "elem"+(i+1), EOBJECT_DESC);
    }
   
    mv.visitInsn(Opcodes.ARETURN);

    mv.visitMaxs(3, arity);
    mv.visitEnd();
  }

  private static void make_blank_bridge(ClassVisitor cw,
      String this_class_name, String super_class_name) {
    MethodVisitor mv;
    mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC
        | Opcodes.ACC_BRIDGE, "blank", "()L" + super_class_name + ";",
        null, null);
    mv.visitCode();
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, this_class_name, "blank",
        "()L" + this_class_name + ";");
    mv.visitInsn(Opcodes.ARETURN);
    mv.visitMaxs(1, 1);
    mv.visitEnd();
  }

  private static void create_tuple_nth(int n_cells, ClassVisitor cw,
      String this_class_name) {
    MethodVisitor mv;
    mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "elm", "(I)"
        + ETERM_TYPE.getDescriptor(), null, null);
    mv.visitCode();

    Label dflt = new Label();
    Label[] labels = new Label[n_cells];
    for (int i = 0; i < n_cells; i++) {
      labels[i] = new Label();
    }

    mv.visitVarInsn(Opcodes.ILOAD, 1);
    mv.visitTableSwitchInsn(1, n_cells, dflt, labels);

    for (int zbase = 0; zbase < n_cells; zbase++) {

      mv.visitLabel(labels[zbase]);

      mv.visitVarInsn(Opcodes.ALOAD, 0); // load this
      String field = "elem" + (zbase + 1);

      mv.visitFieldInsn(Opcodes.GETFIELD, this_class_name, field,
          ETERM_TYPE.getDescriptor());
      mv.visitInsn(Opcodes.ARETURN);
    }

    mv.visitLabel(dflt);

    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitVarInsn(Opcodes.ILOAD, 1);
    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ETUPLE_NAME, "bad_nth", "(I)"
        + ETERM_TYPE.getDescriptor());
    mv.visitInsn(Opcodes.ARETURN); // make compiler happy

    mv.visitMaxs(3, 2);
    mv.visitEnd();
  }

  private static void create_tuple_set(int n_cells, ClassVisitor cw,
      String this_class_name) {
    MethodVisitor mv;
    mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "set", "(I"
        + ETERM_TYPE.getDescriptor() + ")V", null, null);
    mv.visitCode();

    Label dflt = new Label();
    Label[] labels = new Label[n_cells];
    for (int i = 0; i < n_cells; i++) {
      labels[i] = new Label();
    }

    mv.visitVarInsn(Opcodes.ILOAD, 1);
    mv.visitTableSwitchInsn(1, n_cells, dflt, labels);

    for (int zbase = 0; zbase < n_cells; zbase++) {

      mv.visitLabel(labels[zbase]);

      mv.visitVarInsn(Opcodes.ALOAD, 0); // load this
      mv.visitVarInsn(Opcodes.ALOAD, 2); // load term

      String field = "elem" + (zbase + 1);

      mv.visitFieldInsn(Opcodes.PUTFIELD, this_class_name, field,
          ETERM_TYPE.getDescriptor());
      mv.visitInsn(Opcodes.RETURN);
    }

    mv.visitLabel(dflt);

    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitVarInsn(Opcodes.ILOAD, 1);
    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ETUPLE_NAME, "bad_nth", "(I)"
        + ETERM_TYPE.getDescriptor());
    mv.visitInsn(Opcodes.POP);
    mv.visitInsn(Opcodes.RETURN); // make compiler happy

    mv.visitMaxs(3, 3);
    mv.visitEnd();
  }

  protected final EObject bad_nth(int i) {
    throw ERT.badarg(this);
  }

  private static void create_count(ClassVisitor cw, int n) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "arity", "()I",
        null, null);
    mv.visitCode();

    if (n <= 5) {
      mv.visitInsn(Opcodes.ICONST_0 + n);
    } else {
      mv.visitLdcInsn(new Integer(n));
    }
    mv.visitInsn(Opcodes.IRETURN);
    mv.visitMaxs(1, 1);
    mv.visitEnd();
  }

  private static void create_cast(ClassVisitor cw, int n) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC
        | Opcodes.ACC_STATIC, "cast", "(L" + ETUPLE_NAME + ";)L"
        + ETUPLE_NAME + n + ";", null, null);
    mv.visitCode();

    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ETUPLE_NAME, "arity", "()I");

    if (n <= 5) {
      mv.visitInsn(Opcodes.ICONST_0 + n);
    } else {
      mv.visitLdcInsn(new Integer(n));
    }

    Label fail = new Label();

    mv.visitJumpInsn(Opcodes.IF_ICMPNE, fail);
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitTypeInsn(Opcodes.CHECKCAST, ETUPLE_NAME + n);
    mv.visitInsn(Opcodes.ARETURN);

    mv.visitLabel(fail);
    mv.visitInsn(Opcodes.ACONST_NULL);
    mv.visitInsn(Opcodes.ARETURN);

    mv.visitMaxs(2, 2);
    mv.visitEnd();
  }


  private static void create_cast2(ClassVisitor cw, int n) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC
        | Opcodes.ACC_STATIC, "cast", "(L" + Type.getInternalName(EObject.class) + ";)L"
        + ETUPLE_NAME + n + ";", null, null);
    mv.visitCode();

    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitInsn(Opcodes.DUP);
    mv.visitTypeInsn(Opcodes.INSTANCEOF, ETUPLE_NAME + n);
   
    Label fail = new Label();

    mv.visitJumpInsn(Opcodes.IFEQ, fail);
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitTypeInsn(Opcodes.CHECKCAST, ETUPLE_NAME + n);
    mv.visitInsn(Opcodes.ARETURN);

    mv.visitLabel(fail);
    mv.visitInsn(Opcodes.ACONST_NULL);
    mv.visitInsn(Opcodes.ARETURN);

    mv.visitMaxs(2, 2);
    mv.visitEnd();
  }

  private static void create_constructor(ClassVisitor cw,
      String super_class_name) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V",
        null, null);
    mv.visitCode();
    mv.visitVarInsn(Opcodes.ALOAD, 0); // load this
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, super_class_name, "<init>",
        "()V");
    mv.visitInsn(Opcodes.RETURN);
    mv.visitMaxs(1, 1);
    mv.visitEnd();
  }

  public static void dump(String name, byte[] data) {

    name = name.replace('.'  , '/');
    File out_dir;
   
    int idx = name.lastIndexOf('/');
    if (idx == -1) {
      out_dir = new File("target/woven");
    } else {
    String pkg = name.substring(0, name.lastIndexOf('/'));
    out_dir = new File(new File("target/woven"), pkg);
    }
    out_dir.mkdirs();

    FileOutputStream fo;
    try {
      String fname = "target/woven/" + name + ".class";
      fo = new FileOutputStream(fname);
      fo.write(data);
      fo.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder("{");
    for (int i = 1; i <= this.arity(); i++) {
      if (i != 1)
        sb.append(',');
      sb.append(elm(i));
    }
    sb.append("}");
    return sb.toString();
  }

  /*
   * TODO: Make compatible with Erlang
   */
  @Override
  public int hashCode() {
    int arity = arity();
    int result = arity;
    for (int i = 0; i < arity; i++) {
      result *= 3;
      result += elm(i + 1).hashCode();
    }
    return result;
  }

  @Override
  public boolean equalsExactly(EObject obj) {
    if (obj instanceof ETuple) {
      ETuple ot = (ETuple) obj;
      if (arity() != ot.arity())
        return false;
      for (int i = 0; i < arity(); i++) {
        if (!elm(i + 1).equalsExactly(ot.elm(i + 1)))
          return false;
      }
      return true;
    }
    return false;
  }

  public static final EAtom am_Elixir_Regex = EAtom.intern("Elixir.Regex");
  public static final EAtom am_re_pattern = EAtom.intern("re_pattern");
 
  static String tos(EObject o) {
    EBinary bin = o.testBinary();
    return new String(bin.getByteArray(), IO.UTF8);
  }
 
  @Override
  public Type emit_const(MethodVisitor fa) {

    Type type = Type.getType(this.getClass());

    fa.visitTypeInsn(Opcodes.NEW, type.getInternalName());
    fa.visitInsn(Opcodes.DUP);
    fa.visitMethodInsn(Opcodes.INVOKESPECIAL, type.getInternalName(),
        "<init>", "()V");

    for (int i = 0; i < arity(); i++) {
      fa.visitInsn(Opcodes.DUP);

      if (i == 1 && arity() == 5 && elm(1) == am_Elixir_Regex) {
       
      //  System.err.println("emitting pattern /"+tos(elm(3))+"/"+tos(elm(4)));

        elm(3).emit_const(fa);
        elm(4).emit_const(fa);
        fa.visitMethodInsn(Opcodes.INVOKESTATIC, "erjang/ERT", "compile_elixir_regex",
                  "(Lerjang/EObject;Lerjang/EObject;)Lerjang/EObject;");
      } else {
        ((EObject) elm(i + 1)).emit_const(fa);
      }
     
      fa.visitFieldInsn(Opcodes.PUTFIELD, type.getInternalName(), "elem"
          + (i + 1), ETERM_TYPE.getDescriptor());
    }

    return type;
  }

  /**
   * @param eInputStream
   * @return
   * @throws IOException
   */
  public static ETuple read(EInputStream buf) throws IOException {
    final int arity = buf.read_tuple_head();

    ETuple res = ETuple.make(arity);
    for (int i = 0; i < arity; i++) {
      res.set(i+1, buf.read_any());
    }
    return res;
  }
 
  /* (non-Javadoc)
   * @see erjang.EObject#encode(erjang.EOutputStream)
   */
  @Override
  public void encode(EOutputStream eos) {
        int arity = arity();
       
        EBinary bin;
    ETuple e2;
    if (arity == 5 && elm(1) == am_Elixir_Regex && (e2=elm(2).testTuple()) != null)
        {
      // in short: it's a compiled regex
     
    //  System.err.println("serializing pattern /"+tos(elm(3))+"/"+tos(elm(4)));
          EObject elm2 = ERT.compile_elixir_regex(elm(3), elm(4));
         
        eos.write_tuple_head(arity);
        eos.write_any(elm(1));
        eos.write_any(elm2);
        eos.write_any(elm(3));
        eos.write_any(elm(4));
        eos.write_any(elm(5));
        return;
        }
       
    eos.write_tuple_head(arity);

        for (int i = 1; i <= arity; i++) {
            eos.write_any(elm(i));
        }
  }

 
 
}
TOP

Related Classes of erjang.ETuple

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.