# HG changeset patch # User Lubomir Nerad # Date 1367946074 -7200 # Node ID 6a4ef883e23333f32bb9789511151f14269b7bf7 # Parent f5c9934a252cddcf77235c30b46e590d251767ba Access non-final exported methods through "invoker" diff -r f5c9934a252c -r 6a4ef883e233 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Mon May 06 18:06:08 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Tue May 07 19:01:14 2013 +0200 @@ -2034,6 +2034,30 @@ return getNameAndType(c2.cpx2, 1, arr); } + public MethodData findMethod(String name, String signature) { + for (MethodData md: methods) { + if (md.getName().equals(name) + && md.getInternalSig().equals(signature)) { + return md; + } + } + + // not found + return null; + } + + public FieldData findField(String name, String signature) { + for (FieldData fd: fields) { + if (fd.getName().equals(name) + && fd.getInternalSig().equals(signature)) { + return fd; + } + } + + // not found + return null; + } + static byte[] findAttr(String n, AttrData[] attrs) { for (AttrData ad : attrs) { if (n.equals(ad.getAttrName())) { diff -r f5c9934a252c -r 6a4ef883e233 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon May 06 18:06:08 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue May 07 19:01:14 2013 +0200 @@ -59,8 +59,22 @@ return classOperation; } - protected String accessMember(String object, String mangledName, - String[] fieldInfoName) throws IOException { + protected String accessField(String object, String mangledName, + String[] fieldInfoName) throws IOException { + return object + "." + mangledName; + } + + protected String accessStaticMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + return object + "." + mangledName; + } + + protected String accessVirtualMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { return object + "." + mangledName; } @@ -1207,8 +1221,8 @@ emit(out, "var @2 = @3.call(@1);", smapper.popA(), smapper.pushT(type), - accessMember(mangleClassAccess + "(false)", - "_" + fi[1], fi)); + accessField(mangleClassAccess + "(false)", + "_" + fi[1], fi)); i += 2; break; } @@ -1221,8 +1235,8 @@ emit(out, "@3.call(@2, @1);", smapper.popT(type), smapper.popA(), - accessMember(mangleClassAccess + "(false)", - "_" + fi[1], fi)); + accessField(mangleClassAccess + "(false)", + "_" + fi[1], fi)); i += 2; break; } @@ -1232,9 +1246,9 @@ final int type = VarType.fromFieldType(fi[2].charAt(0)); emit(out, "var @1 = @2();", smapper.pushT(type), - accessMember(accessClass(fi[0].replace('/', '_')) - + "(false)", - "_" + fi[1], fi)); + accessField(accessClass(fi[0].replace('/', '_')) + + "(false)", + "_" + fi[1], fi)); i += 2; addReference(fi[0]); break; @@ -1244,9 +1258,9 @@ String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); emit(out, "@1(@2);", - accessMember(accessClass(fi[0].replace('/', '_')) - + "(false)", - "_" + fi[1], fi), + accessField(accessClass(fi[0].replace('/', '_')) + + "(false)", + "_" + fi[1], fi), smapper.popT(type)); i += 2; addReference(fi[0]); @@ -1492,7 +1506,7 @@ } final String in = mi[0]; - out.append(accessMember( + out.append(accessStaticMethod( accessClass(in.replace('/', '_')) + "(false)", mn.startsWith("cons_") ? "constructor." + mn @@ -1536,7 +1550,7 @@ .append(" = "); } - out.append(accessMember(vars[0].toString(), mn, mi)); + out.append(accessVirtualMethod(vars[0].toString(), mn, mi)); out.append('('); String sep = ""; for (int j = 1; j < numArguments; ++j) { diff -r f5c9934a252c -r 6a4ef883e233 rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java Mon May 06 18:06:08 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java Tue May 07 19:01:14 2013 +0200 @@ -23,6 +23,8 @@ import java.util.Map; import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; @ExtraJavaScript(processByteCode = false, resource="") final class ClassDataCache { @@ -37,7 +39,12 @@ classDataMap = new HashMap(); } - ClassData getClassData(final String className) throws IOException { + ClassData getClassData(String className) throws IOException { + if (className.startsWith("[")) { + // required for accessVirtualMethod, shouldn't be problematic for + // calls from other sources + className = "java/lang/Object"; + } Object cacheEntry = classDataMap.get(className); if (cacheEntry == null) { final InputStream is = loadClass(resources, className); @@ -48,6 +55,58 @@ return (cacheEntry != MISSING_CLASS) ? (ClassData) cacheEntry : null; } + MethodData findMethod(final String startingClass, + final String name, + final String signature) throws IOException { + return findMethod(getClassData(startingClass), name, signature); + } + + FieldData findField(final String startingClass, + final String name, + final String signature) throws IOException { + return findField(getClassData(startingClass), name, signature); + } + + MethodData findMethod(ClassData currentClass, + final String name, + final String signature) throws IOException { + while (currentClass != null) { + final MethodData methodData = + currentClass.findMethod(name, signature); + if (methodData != null) { + return methodData; + } + + final String superClassName = currentClass.getSuperClassName(); + if (superClassName == null) { + break; + } + currentClass = getClassData(superClassName); + } + + return null; + } + + FieldData findField(ClassData currentClass, + final String name, + final String signature) throws IOException { + while (currentClass != null) { + final FieldData fieldData = + currentClass.findField(name, signature); + if (fieldData != null) { + return fieldData; + } + + final String superClassName = currentClass.getSuperClassName(); + if (superClassName == null) { + break; + } + currentClass = getClassData(superClassName); + } + + return null; + } + private static InputStream loadClass(Bck2Brwsr.Resources l, String name) throws IOException { return l.get(name + ".class"); // NOI18N diff -r f5c9934a252c -r 6a4ef883e233 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Mon May 06 18:06:08 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue May 07 19:01:14 2013 +0200 @@ -32,12 +32,14 @@ private final Bck2Brwsr.Resources resources; private final ExportedSymbols exportedSymbols; + private final StringArray invokerMethods; private VM(Appendable out, Bck2Brwsr.Resources resources) { super(out); this.resources = resources; this.classDataCache = new ClassDataCache(resources); this.exportedSymbols = new ExportedSymbols(resources); + this.invokerMethods = new StringArray(); } static { @@ -67,7 +69,20 @@ private void doCompile(StringArray names) throws IOException { generatePrologue(); + out.append( + "\n var invoker = function Invoker() {" + + "\n return Invoker.target[Invoker.method]" + + ".apply(Invoker.target, arguments);" + + "\n };"); generateBody(names); + for (String invokerMethod: invokerMethods.toArray()) { + out.append("\n invoker." + invokerMethod + " = function(target) {" + + "\n invoker.target = target;" + + "\n invoker.method = '" + invokerMethod + "';" + + "\n return invoker;" + + "\n };"); + } + out.append("\n"); generateEpilogue(); } @@ -268,16 +283,64 @@ } @Override - protected String accessMember(String object, String mangledName, - String[] fieldInfoName) throws IOException { - final ClassData declaringClass = + protected String accessField(String object, String mangledName, + String[] fieldInfoName) throws IOException { + final FieldData field = + classDataCache.findField(fieldInfoName[0], + fieldInfoName[1], + fieldInfoName[2]); + return accessNonVirtualMember(object, mangledName, + (field != null) ? field.cls : null); + } + + @Override + protected String accessStaticMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + final MethodData method = + classDataCache.findMethod(fieldInfoName[0], + fieldInfoName[1], + fieldInfoName[2]); + return accessNonVirtualMember(object, mangledName, + (method != null) ? method.cls : null); + } + + @Override + protected String accessVirtualMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + final ClassData referencedClass = classDataCache.getClassData(fieldInfoName[0]); - if (declaringClass == null) { - return object + "['" + mangledName + "']"; + final MethodData method = + classDataCache.findMethod(referencedClass, + fieldInfoName[1], + fieldInfoName[2]); + + if ((method != null) + && (((method.access & ByteCodeParser.ACC_FINAL) != 0) + || ((referencedClass.getAccessFlags() + & ByteCodeParser.ACC_FINAL) != 0) + || !exportedSymbols.isExported(method))) { + return object + "." + mangledName; } - // TODO - return object + "." + mangledName; + return accessThroughInvoker(object, mangledName); + } + + private String accessThroughInvoker(String object, String mangledName) { + if (!invokerMethods.contains(mangledName)) { + invokerMethods.add(mangledName); + } + return "invoker." + mangledName + '(' + object + ')'; + } + + private static String accessNonVirtualMember(String object, + String mangledName, + ClassData declaringClass) { + return (declaringClass != null) ? object + "." + mangledName + : object + "['" + mangledName + "']"; } private static final class Standalone extends VM {