Package adobe.abc

Source Code of adobe.abc.AbcThunkGen$IndentingPrintWriter

/*
*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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 adobe.abc;

import java.io.*;
import java.util.*;

import static macromedia.asc.embedding.avmplus.ActionBlockConstants.*;
import adobe.abc.GlobalOptimizer.*;
import static adobe.abc.OptimizerConstants.*;
import static java.lang.Boolean.TRUE;
import static java.lang.Boolean.FALSE;
/**
* @author Steven Johnson
*/
public class AbcThunkGen
{
  static class IndentingPrintWriter extends PrintWriter
  {
    int indent;
    IndentingPrintWriter(Writer w)
    {
      super(w);
    }
    public void println()
    {
      for (int i=0; i < indent; i++)
        print("    ");
      super.println();
    }
    public void println(String s)
    {
      for (int i=0; i < indent; i++)
        print("    ");
      super.println(s);
    }
  }

  public static void main(String[] args) throws IOException
  {
    if (args.length == 0)
    {
      System.out.println("usage: AbcThunkGen [-import foo.abc] bar.abc");
      return;
    }

     byte[] abcdata = null;
     InputAbc ia = null;
    String filename = null;
    GlobalOptimizer go = new GlobalOptimizer();
    go.ALLOW_NATIVE_CTORS = true;
    for (int i = 0; i < args.length; ++i)
    {
       if(args[i].equals("-import"))
      {
        i++;
        InputAbc imported = go.new InputAbc();
        imported.readAbc(load(args[i]));
         continue;
       }
      if (ia != null)
      {
        throw new RuntimeException("only one abc file may be specified");
      }
      filename = args[i];
      abcdata = load(filename);
      ia = go.new InputAbc();
      ia.readAbc(abcdata);
    }

    if (ia == null)
    {
      throw new RuntimeException("an abc file must be specified");
    }
   
    String scriptname = filename.substring(0,filename.lastIndexOf('.'));
    emitNatives(ia, abcdata, scriptname, TypeCache.instance().namespaceNames);
  }

  static byte[] load(String filename) throws IOException
  {
    InputStream in = new FileInputStream(filename);
    try
    {
      byte[] before = new byte[in.available()];
      in.read(before);
      return before;
    }
    finally
    {
      in.close();
    }
  }
 
  private InputAbc abc;
  private byte[] abcdata;
  private String name;
  private Map<Namespace,Name> namespaceNames;
  private PrintWriter out_h;
  private IndentingPrintWriter out_c;
  private Map<Integer,String> native_methods;
  private HashMap<Type,Integer> class_id_map;
  private HashMap<String, HashMap<String, Method>> unique_thunks;

  AbcThunkGen(InputAbc abc, byte[] abcdata, String name, Map<Namespace,Name> namespaceNames,
          PrintWriter out_h, IndentingPrintWriter out_c)
  {
    this.abc = abc;
    this.abcdata = abcdata;
    this.name = name;
    this.namespaceNames = namespaceNames;
    this.out_h = out_h;
    this.out_c = out_c;
    this.native_methods = new TreeMap<Integer,String>();
    this.unique_thunks = new HashMap<String, HashMap<String, Method>>();
    this.class_id_map = new HashMap<Type,Integer>();
    for (int i = 0; i < abc.classes.length; ++i)
      this.class_id_map.put(abc.classes[i], i);
  }

  static void emitNatives(InputAbc abc, byte[] abcdata, String name,
    Map<Namespace,Name> namespaceNames) throws IOException
  {
    PrintWriter out_h = new PrintWriter(new FileWriter(name+".h2"));
    IndentingPrintWriter out_c = new IndentingPrintWriter(new FileWriter(name+".cpp2"));
    try
    {
      AbcThunkGen ngen = new AbcThunkGen(abc, abcdata, name, namespaceNames, out_h, out_c);
      ngen.emit();
    }
    finally
    {
      out_c.close();
      out_h.close();
    }
  }
 
  private void emit()
  {
    // header file - definitions & native method decls
    out_h.println("/* machine generated file -- do not edit */");
    out_c.println("/* machine generated file -- do not edit */");

    out_h.println("#define AVMTHUNK_VERSION 1");
   
    out_h.printf("const uint32_t %s_abc_class_count = %d;\n",name,abc.classes.length);
    out_h.printf("const uint32_t %s_abc_script_count = %d;\n",name,abc.scripts.length);
    out_h.printf("const uint32_t %s_abc_method_count = %d;\n",name,abc.methods.length);
    out_h.printf("const uint32_t %s_abc_length = %d;\n",name,abcdata.length);
    out_h.printf("extern const uint8_t %s_abc_data[%d];\n",name,abcdata.length);

    for (int i = 0; i < abc.scripts.length; ++i)
    {
      Type s = abc.scripts[i];
      // not enough info in the ABC to recover the original name (eg abcpackage_Foo_as)
      // so output identifiers based on the native script functions found
      for (Binding bb : s.defs.values())
      {
        if (bb.method != null && bb.method.isNative())
          out_h.println("const uint32_t abcscript_"+ bb.getName() + " = " + i + ";");
      }
      emitSourceTraits("", s);
    }
   
    out_c.println("// "+unique_thunks.size()+" unique thunks");
    for (String sig: unique_thunks.keySet())
    {
      out_c.println();
      HashMap<String, Method> users = unique_thunks.get(sig);
      assert(users.size() > 0);
      Method m = null;
      for (String native_name: users.keySet())
      {
        out_c.println("// "+native_name);
        m = users.get(native_name);
      }
      String thunkname = name+"_"+sig;
      // emit both with and without-cookie versions, since we can't tell at this point which
      // might be used for a particular method. rely on linker to strip the unused ones.
      emitThunk(thunkname, m, false);
      emitThunk(thunkname, m, true);
      for (String native_name: users.keySet())
      {
        m = users.get(native_name);
        out_h.printf("  const uint32_t %s = %d;\n", native_name, m.id);
        // use #define here (rather than constants) to avoid the linker including them and thus preventing dead-stripping
        // (sad but true, happens in some environments)
        out_h.printf("  #define %s_thunk  %s_thunk\n", native_name, thunkname);
        out_h.printf("  #define %s_thunkc %s_thunkc\n", native_name, thunkname);
      }
    }

    // cpp file - abc data, thunks
    out_c.println("const uint8_t "+name+"_abc_data["+abcdata.length+"] = {");
    for (int i=0, n=abcdata.length; i < n; i++)
    {
      int x = abcdata[i] & 255;
      if (x < 10) out_c.print("  ");
      else if (x < 100) out_c.print(' ');
      out_c.print(x);
      if (i+1 < n) out_c.print(", ");
      if (i%16 == 15) out_c.println();
    }
    out_c.println("};");
  }

  void emitSourceTraits(String prefix, Type s)
  {
    if (s.init != null && s.init.isNative())
    {
      String native_name = prefix + s.getName();
      gatherThunk(native_name, s.init);
    }
    for (Binding b: s.defs.values())
    {
      Namespace ns = b.getName().nsset(0);
      String id = prefix + propLabel(b, ns);
      String ctype = null;

      if (b.method != null)
      {
        if(b.method.isNative())
          emitSourceMethod(prefix, b, ns);
      }
      else if (GlobalOptimizer.isClass(b))
      {
        emitSourceClass(b, ns);
      }
    }
  }
 
  static String to_cname(String nm)
  {
    // munge symbols that will make C unhappy
    nm = nm.replace("+", "_");
    nm = nm.replace("-", "_");
    nm = nm.replace("?", "_");
    nm = nm.replace("!", "_");
    nm = nm.replace("<", "_");
    nm = nm.replace(">", "_");
    nm = nm.replace("=", "_");
    nm = nm.replace("(", "_");
    nm = nm.replace(")", "_");
    nm = nm.replace("\"", "_");
    nm = nm.replace("'", "_");
    nm = nm.replace("*", "_");
    nm = nm.replace(" ", "_");
    nm = nm.replace(".", "_");
    nm = nm.replace("$", "_");
    nm = nm.replace(":", "_");
    nm = nm.replace("/", "_");
    return nm;
  }

  void emitSourceClass(Binding b, Namespace ns)
  {
    String label = ns_prefix(ns, true) + to_cname(b.getName().name);

    Type c = b.type.t;
   
    out_h.println();
    out_h.println("const uint32_t abcclass_"+ label + " = " + class_id_map.get(c) + ";");

    emitSourceTraits(label+"_", c);
    emitSourceTraits(label+"_", c.itype);
  }
 
  String ctype_i(int ctype, boolean allowObject)
  {
    switch (ctype)
    {
      case CTYPE_OBJECT: 
        if (allowObject)
          return "AvmObject";
        // else fall thru
      case CTYPE_ATOM:   
        return "AvmBox";
      case CTYPE_VOID:   
        return "void";
      case CTYPE_BOOLEAN:   
        return "AvmBoolArg";
      case CTYPE_INT:     
        return "int32_t";
      case CTYPE_UINT:   
        return "uint32_t";
      case CTYPE_DOUBLE:   
        return "double";
      case CTYPE_STRING:   
        return "AvmString";
      case CTYPE_NAMESPACE: 
        return "AvmNamespace";
      default:
        assert(false);
        return "";
    }
  }

  void emitSourceMethod(String prefix, Binding b, Namespace ns)
  {
    Method m = b.method;

    String native_name = prefix + propLabel(b, ns);

    if (GlobalOptimizer.isGetter(b))
      native_name += "_get";
    else if (GlobalOptimizer.isSetter(b))
      native_name += "_set";
   
    gatherThunk(native_name, m);
  }

  String ns_prefix(Namespace ns, boolean iscls)
  {
    if (!ns.isPublic() && !ns.isInternal())
    {
      if (ns.isPrivate() && !iscls) return "private_";
      if (ns.isProtected()) return "protected_";
      if (namespaceNames.containsKey(ns)) return namespaceNames.get(ns) + "_";
    }
    String p = to_cname(ns.uri);
    if (p.length() > 0) p += "_";
    return p;
  }
 
  String propLabel(Binding b, Namespace ns)
  {
    return ns_prefix(ns, false) + b.getName().name;
  }

  int defValCType(Object value)
  {
    if (value instanceof Integer)
      return CTYPE_INT;
    if (value instanceof Long)
      return CTYPE_UINT;
    if (value instanceof Double)
    {
      double d = (Double)value;
      if (d == (int)(d))
        return CTYPE_INT;
      if (d == (long)(d))
        return CTYPE_UINT;
      return CTYPE_DOUBLE;
    }
    if (value instanceof String)
      return CTYPE_STRING;
// sorry, not supported in natives
//    if (value instanceof Namespace)
//      return CTYPE_NAMESPACE;
    if (value == TRUE)
      return CTYPE_BOOLEAN;
    if (value == FALSE)
      return CTYPE_BOOLEAN;
    if (value.toString() == "undefined")
      return CTYPE_ATOM;
    if (value.toString() == "null")
      return CTYPE_ATOM;
    throw new RuntimeException("unsupported default-value type "+value.toString());
  }

  String defValStr(Object value)
  {
    // for numeric values, just emit inline rather than looking up in const pool
    if (value instanceof Integer)
      return value.toString();
    if (value instanceof Long)
      return value.toString();
    if (value instanceof Double)
    {
      double d = (Double)value;
      if (d == (int)(d))
        return Integer.toString((int)d, 10);
      if (d == (long)(d))
        return Long.toString((long)d, 10) + "U";
      if (Double.isInfinite(d))
        return (d < 0.0) ? "kAvmThunkNegInfinity" : "kAvmThunkInfinity";
      if (Double.isNaN(d))
        return "kAvmThunkNaN";
      return value.toString();
    }
    if (value instanceof String)
    {
      for (int i = 0; i < abc.strings.length; ++i)
        if (abc.strings[i].equals(value))
          return "AvmThunkConstant_AvmString("+Integer.toString(i, 10)+")/* \""+abc.strings[i]+"\" */";
    }
// sorry, not supported in natives
//    if (value instanceof Namespace)
//      return value.toString();
    if (value == TRUE)
      return "true";
    if (value == FALSE)
      return "false";
    if (value.toString() == "undefined")
      return "kAvmThunkUndefined";
    if (value.toString() == "null")
      return "kAvmThunkNull";
    throw new RuntimeException("unsupported default-value type "+value.toString());
  }

  int get_optional_count(Method m)
  {
    int optional_count = 0;
    if (m.values != null)
      for (Object v : m.values)
        if (v != null)
          optional_count++;
    return optional_count;
  }
 
  String sigChar(int ctype, boolean allowObject)
  {
    switch (ctype)
    {
      case CTYPE_OBJECT:   
        if (allowObject)
          return "o";
        // else fall thru
      case CTYPE_ATOM:   
        return "a";
      case CTYPE_VOID:   
        return "v";
      case CTYPE_BOOLEAN:   
        return "b";
      case CTYPE_INT:     
        return "i";
      case CTYPE_UINT:   
        return "u";
      case CTYPE_DOUBLE:   
        return "d";
      case CTYPE_STRING:   
        return "s";
      case CTYPE_NAMESPACE: 
        return "n";
      default:
        assert(false);
        return "";
    }
  }

  String thunkSig(Method m)
  {
    String sig = sigChar(m.returns.t.ctype, false)+"2";
    if (m.returns.t.ctype == CTYPE_DOUBLE)
      sig += sigChar(CTYPE_DOUBLE, false);
    else
      sig += sigChar(CTYPE_ATOM, false);
    sig += "_";
    for (int i = 0; i < m.getParams().length; ++i)
    {
      sig += sigChar(m.getParams()[i].t.ctype, true);
    }
    if (m.hasOptional())
    {
      int param_count = m.getParams().length - 1;
      int optional_count = get_optional_count(m);
      for (int i = param_count - optional_count + 1; i <= param_count; i++)
      {
        String dts = sigChar(defValCType(m.values[i]), true);
        String defval = to_cname(defValStr(m.values[i]));
        sig += "_opt" + dts + defval;
      }
    }
    else
    {
      assert(get_optional_count(m) == 0);
    }
    if (m.needsRest())
      sig += "_rest";
    return sig;
  }

  void gatherThunk(String native_name, Method m)
  {
    native_methods.put(m.id, native_name);
   
    String sig = thunkSig(m);
    if (!unique_thunks.containsKey(sig))
      unique_thunks.put(sig, new HashMap<String, Method>());
    unique_thunks.get(sig).put(native_name, m);
  }

  void emitThunk(String name, Method m, boolean cookie)
  {
    String ret = ctype_i(m.returns.t.ctype, false);
   
    out_h.printf("extern AvmThunkRetType_%s AVMTHUNK_CALLTYPE %s_thunk%s(AvmMethodEnv env, uint32_t argc, const AvmBox* argv);\n", ret, name, cookie?"c":"");

    out_c.printf("AvmThunkRetType_%s AVMTHUNK_CALLTYPE %s_thunk%s(AvmMethodEnv env, uint32_t argc, const AvmBox* argv)\n", ret, name, cookie?"c":"");
    out_c.println("{");
    out_c.indent++;

    int param_count = m.getParams().length-1;
    assert(param_count >= 0);
    int optional_count = get_optional_count(m);
    assert(optional_count <= param_count);

    String argszprev = "0";
    for (int i = 0; i < m.getParams().length; ++i)
    {
      String cts = ctype_i(m.getParams()[i].t.ctype, true);
      if (i == 0)
        out_c.println("const uint32_t argoff0 = 0;");
      else
        out_c.println("const uint32_t argoff"+i+" = argoff"+(i-1)+" + "+argszprev+";");
      argszprev = "AvmThunkArgSize_"+cts;
    }
    if (m.needsRest())
    {
      out_c.println("const uint32_t argoffV = argoff"+(m.getParams().length-1)+" + "+argszprev+";");
    }
   
    int argct = m.getParams().length + (cookie?1:0) + (m.needsRest()?2:0);
    String[] argvals = new String[argct];
    String[] argtypes = new String[argct];
    int argsidx = 0;
    for (int i = 0; i < m.getParams().length; ++i)
    {
      String cts = ctype_i(m.getParams()[i].t.ctype, true);
      String val = "AvmThunkUnbox_"+cts+"(argv[argoff" + i + "])";
      if (i > param_count - optional_count)
      {
        String dts = ctype_i(defValCType(m.values[i]), true);
        String defval = defValStr(m.values[i]);
        if (!dts.equals(cts))
          defval = "AvmThunkCoerce_"+dts+"_"+cts+"("+defval+")";
        val = "(argc < "+i+" ? "+defval+" : "+val+")";
      }
      argvals[argsidx] = val;
      argtypes[argsidx] = cts;
      argsidx++;
      if (i == 0 && cookie)
      {
        argvals[argsidx] = "AVMTHUNK_GET_COOKIE(env)";
        argtypes[argsidx] = "int32_t";
        argsidx++;
      }
    }
 
    if (m.needsRest())
    {
     
      argvals[argsidx] = "(argc <= "+param_count+" ? NULL : argv + argoffV)";
      argtypes[argsidx] = "const AvmBox*";
      argsidx++;

      argvals[argsidx] = "(argc <= "+param_count+" ? 0 : argc - "+param_count+")";
      argtypes[argsidx] = "uint32_t";
      argsidx++;
    }

    if (!m.hasOptional() && !m.needsRest())
      out_c.println("(void)argc;");

    out_c.println("AVMTHUNK_DEBUG_ENTER(env)");
   
    String call = "";
    if (m.returns.t.ctype != CTYPE_VOID)
      call += "const "+ret+" ret = ";   
    call += "AVMTHUNK_CALL_FUNCTION_"+(argvals.length-1)+"(AVMTHUNK_GET_HANDLER(env), "+ret;
    out_c.println(call);
    out_c.indent++;
    for (int i = 0; i < argvals.length; ++i)
    {
      out_c.println(", " + argtypes[i] + ", " + argvals[i]);
    }
    out_c.indent--;
    out_c.println(");");

    out_c.println("AVMTHUNK_DEBUG_EXIT(env)");

    out_c.println("return AvmToRetType_"+ret+"(ret);");
    out_c.indent--;
    out_c.println("}");
  }
}
TOP

Related Classes of adobe.abc.AbcThunkGen$IndentingPrintWriter

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.