diff --git a/rt/emul/compact/src/main/java/java/lang/Package.java b/rt/emul/compact/src/main/java/java/lang/Package.java new file mode 100644 index 000000000..9d9fe0391 --- /dev/null +++ b/rt/emul/compact/src/main/java/java/lang/Package.java @@ -0,0 +1,506 @@ +package java.lang; +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + + +import java.lang.reflect.AnnotatedElement; +import java.io.InputStream; +import java.util.Enumeration; + +import java.util.StringTokenizer; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; + +import java.lang.annotation.Annotation; + +/** + * {@code Package} objects contain version information + * about the implementation and specification of a Java package. + * This versioning information is retrieved and made available + * by the {@link ClassLoader} instance that + * loaded the class(es). Typically, it is stored in the manifest that is + * distributed with the classes. + * + *

The set of classes that make up the package may implement a + * particular specification and if so the specification title, version number, + * and vendor strings identify that specification. + * An application can ask if the package is + * compatible with a particular version, see the {@link + * #isCompatibleWith isCompatibleWith} + * method for details. + * + *

Specification version numbers use a syntax that consists of nonnegative + * decimal integers separated by periods ".", for example "2.0" or + * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent + * major, minor, micro, etc. versions. The version specification is described + * by the following formal grammar: + *

+ *
+ *
SpecificationVersion: + *
Digits RefinedVersionopt + + *
RefinedVersion: + *
{@code .} Digits + *
{@code .} Digits RefinedVersion + * + *
Digits: + *
Digit + *
Digits + * + *
Digit: + *
any character for which {@link Character#isDigit} returns {@code true}, + * e.g. 0, 1, 2, ... + *
+ *
+ * + *

The implementation title, version, and vendor strings identify an + * implementation and are made available conveniently to enable accurate + * reporting of the packages involved when a problem occurs. The contents + * all three implementation strings are vendor specific. The + * implementation version strings have no specified syntax and should + * only be compared for equality with desired version identifiers. + * + *

Within each {@code ClassLoader} instance all classes from the same + * java package have the same Package object. The static methods allow a package + * to be found by name or the set of all packages known to the current class + * loader to be found. + * + * @see ClassLoader#definePackage + */ +public class Package implements java.lang.reflect.AnnotatedElement { + /** + * Return the name of this package. + * + * @return The fully-qualified name of this package as defined in section 6.5.3 of + * The Java™ Language Specification, + * for example, {@code java.lang} + */ + public String getName() { + return pkgName; + } + + + /** + * Return the title of the specification that this package implements. + * @return the specification title, null is returned if it is not known. + */ + public String getSpecificationTitle() { + return specTitle; + } + + /** + * Returns the version number of the specification + * that this package implements. + * This version string must be a sequence of nonnegative decimal + * integers separated by "."'s and may have leading zeros. + * When version strings are compared the most significant + * numbers are compared. + * @return the specification version, null is returned if it is not known. + */ + public String getSpecificationVersion() { + return specVersion; + } + + /** + * Return the name of the organization, vendor, + * or company that owns and maintains the specification + * of the classes that implement this package. + * @return the specification vendor, null is returned if it is not known. + */ + public String getSpecificationVendor() { + return specVendor; + } + + /** + * Return the title of this package. + * @return the title of the implementation, null is returned if it is not known. + */ + public String getImplementationTitle() { + return implTitle; + } + + /** + * Return the version of this implementation. It consists of any string + * assigned by the vendor of this implementation and does + * not have any particular syntax specified or expected by the Java + * runtime. It may be compared for equality with other + * package version strings used for this implementation + * by this vendor for this package. + * @return the version of the implementation, null is returned if it is not known. + */ + public String getImplementationVersion() { + return implVersion; + } + + /** + * Returns the name of the organization, + * vendor or company that provided this implementation. + * @return the vendor that implemented this package.. + */ + public String getImplementationVendor() { + return implVendor; + } + + /** + * Returns true if this package is sealed. + * + * @return true if the package is sealed, false otherwise + */ + public boolean isSealed() { + return sealBase != null; + } + + /** + * Returns true if this package is sealed with respect to the specified + * code source url. + * + * @param url the code source url + * @return true if this package is sealed with respect to url + */ + public boolean isSealed(URL url) { + return url.equals(sealBase); + } + + /** + * Compare this package's specification version with a + * desired version. It returns true if + * this packages specification version number is greater than or equal + * to the desired version number.

+ * + * Version numbers are compared by sequentially comparing corresponding + * components of the desired and specification strings. + * Each component is converted as a decimal integer and the values + * compared. + * If the specification value is greater than the desired + * value true is returned. If the value is less false is returned. + * If the values are equal the period is skipped and the next pair of + * components is compared. + * + * @param desired the version string of the desired version. + * @return true if this package's version number is greater + * than or equal to the desired version number + * + * @exception NumberFormatException if the desired or current version + * is not of the correct dotted form. + */ + public boolean isCompatibleWith(String desired) + throws NumberFormatException + { + if (specVersion == null || specVersion.length() < 1) { + throw new NumberFormatException("Empty version string"); + } + + String [] sa = specVersion.split("\\.", -1); + int [] si = new int[sa.length]; + for (int i = 0; i < sa.length; i++) { + si[i] = Integer.parseInt(sa[i]); + if (si[i] < 0) + throw NumberFormatException.forInputString("" + si[i]); + } + + String [] da = desired.split("\\.", -1); + int [] di = new int[da.length]; + for (int i = 0; i < da.length; i++) { + di[i] = Integer.parseInt(da[i]); + if (di[i] < 0) + throw NumberFormatException.forInputString("" + di[i]); + } + + int len = Math.max(di.length, si.length); + for (int i = 0; i < len; i++) { + int d = (i < di.length ? di[i] : 0); + int s = (i < si.length ? si[i] : 0); + if (s < d) + return false; + if (s > d) + return true; + } + return true; + } + + /** + * Find a package by name in the callers {@code ClassLoader} instance. + * The callers {@code ClassLoader} instance is used to find the package + * instance corresponding to the named class. If the callers + * {@code ClassLoader} instance is null then the set of packages loaded + * by the system {@code ClassLoader} instance is searched to find the + * named package.

+ * + * Packages have attributes for versions and specifications only if the class + * loader created the package instance with the appropriate attributes. Typically, + * those attributes are defined in the manifests that accompany the classes. + * + * @param name a package name, for example, java.lang. + * @return the package of the requested name. It may be null if no package + * information is available from the archive or codebase. + */ + public static Package getPackage(String name) { + return getSystemPackage(name); + } + + /** + * Get all the packages currently known for the caller's {@code ClassLoader} + * instance. Those packages correspond to classes loaded via or accessible by + * name to that {@code ClassLoader} instance. If the caller's + * {@code ClassLoader} instance is the bootstrap {@code ClassLoader} + * instance, which may be represented by {@code null} in some implementations, + * only packages corresponding to classes loaded by the bootstrap + * {@code ClassLoader} instance will be returned. + * + * @return a new array of packages known to the callers {@code ClassLoader} + * instance. An zero length array is returned if none are known. + */ + public static Package[] getPackages() { + return getSystemPackages(); + } + + /** + * Get the package for the specified class. + * The class's class loader is used to find the package instance + * corresponding to the specified class. If the class loader + * is the bootstrap class loader, which may be represented by + * {@code null} in some implementations, then the set of packages + * loaded by the bootstrap class loader is searched to find the package. + *

+ * Packages have attributes for versions and specifications only + * if the class loader created the package + * instance with the appropriate attributes. Typically those + * attributes are defined in the manifests that accompany + * the classes. + * + * @param c the class to get the package of. + * @return the package of the class. It may be null if no package + * information is available from the archive or codebase. */ + static Package getPackage(Class c) { + String name = c.getName(); + int i = name.lastIndexOf('.'); + if (i != -1) { + name = name.substring(0, i); + return getSystemPackage(name); + } else { + return null; + } + } + + /** + * Return the hash code computed from the package name. + * @return the hash code computed from the package name. + */ + public int hashCode(){ + return pkgName.hashCode(); + } + + /** + * Returns the string representation of this Package. + * Its value is the string "package " and the package name. + * If the package title is defined it is appended. + * If the package version is defined it is appended. + * @return the string representation of the package. + */ + public String toString() { + String spec = specTitle; + String ver = specVersion; + if (spec != null && spec.length() > 0) + spec = ", " + spec; + else + spec = ""; + if (ver != null && ver.length() > 0) + ver = ", version " + ver; + else + ver = ""; + return "package " + pkgName + spec + ver; + } + + private Class getPackageInfo() { + if (packageInfo == null) { + try { + packageInfo = Class.forName(pkgName + ".package-info", false, loader); + } catch (ClassNotFoundException ex) { + // store a proxy for the package info that has no annotations + class PackageInfoProxy {} + packageInfo = PackageInfoProxy.class; + } + } + return packageInfo; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.5 + */ + public A getAnnotation(Class annotationClass) { + return getPackageInfo().getAnnotation(annotationClass); + } + + /** + * {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @since 1.5 + */ + @Override + public boolean isAnnotationPresent(Class annotationClass) { + return AnnotatedElement.super.isAnnotationPresent(annotationClass); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.8 + */ + @Override + public A[] getAnnotationsByType(Class annotationClass) { + return getPackageInfo().getAnnotationsByType(annotationClass); + } + + /** + * @since 1.5 + */ + public Annotation[] getAnnotations() { + return getPackageInfo().getAnnotations(); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.8 + */ + @Override + public A getDeclaredAnnotation(Class annotationClass) { + return getPackageInfo().getDeclaredAnnotation(annotationClass); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.8 + */ + @Override + public A[] getDeclaredAnnotationsByType(Class annotationClass) { + return getPackageInfo().getDeclaredAnnotationsByType(annotationClass); + } + + /** + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() { + return getPackageInfo().getDeclaredAnnotations(); + } + + /** + * Construct a package instance with the specified version + * information. + * @param name the name of the package + * @param spectitle the title of the specification + * @param specversion the version of the specification + * @param specvendor the organization that maintains the specification + * @param impltitle the title of the implementation + * @param implversion the version of the implementation + * @param implvendor the organization that maintains the implementation + */ + Package(String name, + String spectitle, String specversion, String specvendor, + String impltitle, String implversion, String implvendor, + URL sealbase, ClassLoader loader) + { + pkgName = name; + implTitle = impltitle; + implVersion = implversion; + implVendor = implvendor; + specTitle = spectitle; + specVersion = specversion; + specVendor = specvendor; + sealBase = sealbase; + this.loader = loader; + } + + /* + * Construct a package using the attributes from the specified manifest. + * + * @param name the package name + * @param man the optional manifest for the package + * @param url the optional code source url for the package + */ + private Package(String name, Object man, URL url, ClassLoader loader) { + String path = name.replace('.', '/').concat("/"); + pkgName = name; + implTitle = null; + implVersion = null; + implVendor = null; + specTitle = null; + specVersion = null; + specVendor = null; + sealBase = null; + this.loader = loader; + } + + /* + * Returns the loaded system package for the specified name. + */ + static Package getSystemPackage(String name) { + synchronized (pkgs) { + Package pkg = pkgs.get(name); + if (pkg == null) { + pkg = new Package(name, null, null, null); + pkgs.put(name, pkg); + } + return pkg; + } + } + + /* + * Return an array of loaded system packages. + */ + static Package[] getSystemPackages() { + // First, update the system package map with new package names + synchronized (pkgs) { + return pkgs.values().toArray(new Package[pkgs.size()]); + } + } + + private static Map pkgs = new HashMap<>(31); + + + /* + * Private storage for the package name and attributes. + */ + private final String pkgName; + private final String specTitle; + private final String specVersion; + private final String specVendor; + private final String implTitle; + private final String implVersion; + private final String implVendor; + private final URL sealBase; + private transient final ClassLoader loader; + private transient Class packageInfo; +} diff --git a/rt/emul/compacttest/pom.xml b/rt/emul/compacttest/pom.xml index 128181f28..497a76c55 100644 --- a/rt/emul/compacttest/pom.xml +++ b/rt/emul/compacttest/pom.xml @@ -71,6 +71,18 @@ org-openide-util-lookup test + + org.slf4j + slf4j-api + 1.5.6 + test + + + org.slf4j + slf4j-simple + 1.5.6 + test + @@ -100,7 +112,7 @@ - .* + .*(emul|launch).* diff --git a/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/compact/tck/Target15Test.java b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/compact/tck/Target15Test.java new file mode 100644 index 000000000..a6802c1aa --- /dev/null +++ b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/compact/tck/Target15Test.java @@ -0,0 +1,55 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.impl.StaticLoggerBinder; +import org.slf4j.spi.LoggerFactoryBinder; +import org.testng.annotations.Factory; + +public class Target15Test { + @Compare + public String loadLoggerFactoryBinder() { + Class c = LoggerFactoryBinder.class; + return c.getName(); + } + + @Compare + public String hasBinder() { + StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); + if (binder == null) { + throw new IllegalStateException("No binder: " + binder); + } + return binder.getClass().getName(); + } + + @Compare + public String log() { + Logger log = LoggerFactory.getLogger(Target15Test.class); + log.trace("Hi there!"); + return log.getName(); + } + + @Factory + public static Object[] create() { + return VMTest.create(Target15Test.class); + } +} diff --git a/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/PackagesTest.java b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/PackagesTest.java new file mode 100644 index 000000000..0e4bd85fa --- /dev/null +++ b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/PackagesTest.java @@ -0,0 +1,53 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class PackagesTest { + class Inner { + } + + @Compare + public String topMostPackage() { + return getClass().getPackage().getName(); + } + + @Compare + public String innerPackage() { + return new Inner().getClass().getPackage().getName(); + } + + @Compare + public boolean areTheSame() { + Package p1 = getClass().getPackage(); + Package p2 = new Inner().getClass().getPackage(); + return p1 == p2; + } + + @Factory + public static Object[] create() { + return VMTest.create(PackagesTest.class); + } +} diff --git a/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java index 9fe3a04f3..ff9db63db 100644 --- a/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java +++ b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java @@ -55,7 +55,7 @@ public class ResourcesTest { } } assert different : "Not all manifests should look like first one:\n" + first; - if (cnt > 40 && cnt < 50) { + if (cnt > 40 && cnt < 60) { return "OK"; } else { return "" + cnt; diff --git a/rt/emul/fake/src/main/java/java/lang/Package.java b/rt/emul/fake/src/main/java/java/lang/Package.java new file mode 100644 index 000000000..0798c78a9 --- /dev/null +++ b/rt/emul/fake/src/main/java/java/lang/Package.java @@ -0,0 +1,27 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package java.lang; + +public final class Package { + private Package() { + } + + public static Package getPackage(String name) { + return null; + } +} diff --git a/rt/emul/fake/src/test/java/org/apidesign/bck2brwsr/emul/fake/JavaLangTest.java b/rt/emul/fake/src/test/java/org/apidesign/bck2brwsr/emul/fake/JavaLangTest.java new file mode 100644 index 000000000..070b24731 --- /dev/null +++ b/rt/emul/fake/src/test/java/org/apidesign/bck2brwsr/emul/fake/JavaLangTest.java @@ -0,0 +1,27 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.emul.fake; + +import org.testng.annotations.Test; + +public class JavaLangTest { + @Test + public void checkPackage() throws Exception { + JavaUtilTest.assertSignatures(Package.class); + } +} diff --git a/rt/emul/mini/src/main/java/java/lang/Class.java b/rt/emul/mini/src/main/java/java/lang/Class.java index b953b9156..2664a092c 100644 --- a/rt/emul/mini/src/main/java/java/lang/Class.java +++ b/rt/emul/mini/src/main/java/java/lang/Class.java @@ -36,6 +36,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.net.URL; +import java.util.Objects; import org.apidesign.bck2brwsr.core.Exported; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.core.JavaScriptOnly; @@ -1955,5 +1956,18 @@ private static void bck2BrwsrCnvrtVM() { ) private static void castEx() { } - + + public Package getPackage() { + if (isPrimitive() || isArray()) { + return null; + } + + String jvmName = jvmName(); + int lastSlash = jvmName.lastIndexOf('/'); + if (lastSlash == -1) { + return Package.getPackage(""); + } + String name = jvmName.substring(0, lastSlash).replace('/', '.'); + return Package.getPackage(name); + } } diff --git a/rt/emul/mini/src/main/java/java/lang/reflect/AnnotatedElement.java b/rt/emul/mini/src/main/java/java/lang/reflect/AnnotatedElement.java index dc2448316..8338fb87f 100644 --- a/rt/emul/mini/src/main/java/java/lang/reflect/AnnotatedElement.java +++ b/rt/emul/mini/src/main/java/java/lang/reflect/AnnotatedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,9 +31,170 @@ * Represents an annotated element of the program currently running in this * VM. This interface allows annotations to be read reflectively. All * annotations returned by methods in this interface are immutable and - * serializable. It is permissible for the caller to modify the - * arrays returned by accessors for array-valued enum members; it will - * have no affect on the arrays returned to other callers. + * serializable. The arrays returned by methods of this interface may be modified + * by callers without affecting the arrays returned to other callers. + * + *

The {@link #getAnnotationsByType(Class)} and {@link + * #getDeclaredAnnotationsByType(Class)} methods support multiple + * annotations of the same type on an element. If the argument to + * either method is a repeatable annotation type (JLS 9.6), then the + * method will "look through" a container annotation (JLS 9.7), if + * present, and return any annotations inside the container. Container + * annotations may be generated at compile-time to wrap multiple + * annotations of the argument type. + * + *

The terms directly present, indirectly present, + * present, and associated are used throughout this + * interface to describe precisely which annotations are returned by + * methods: + * + *

+ * + *

The table below summarizes which kind of annotation presence + * different methods in this interface examine. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Overview of kind of presence detected by different AnnotatedElement methods
Kind of Presence
MethodDirectly PresentIndirectly PresentPresentAssociated
{@code T}{@link #getAnnotation(Class) getAnnotation(Class<T>)} + * X
{@code Annotation[]}{@link #getAnnotations getAnnotations()} + * X
{@code T[]}{@link #getAnnotationsByType(Class) getAnnotationsByType(Class<T>)} + * X
{@code T}{@link #getDeclaredAnnotation(Class) getDeclaredAnnotation(Class<T>)} + * X
{@code Annotation[]}{@link #getDeclaredAnnotations getDeclaredAnnotations()} + * X
{@code T[]}{@link #getDeclaredAnnotationsByType(Class) getDeclaredAnnotationsByType(Class<T>)} + * XX
+ * + *

For an invocation of {@code get[Declared]AnnotationsByType( Class < + * T >)}, the order of annotations which are directly or indirectly + * present on an element E is computed as if indirectly present + * annotations on E are directly present on E in place + * of their container annotation, in the order in which they appear in + * the value element of the container annotation. + * + *

There are several compatibility concerns to keep in mind if an + * annotation type T is originally not repeatable and + * later modified to be repeatable. + * + * The containing annotation type for T is TC. + * + *

* *

If an annotation returned by a method in this interface contains * (directly or indirectly) a {@link Class}-valued member referring to @@ -45,14 +206,20 @@ * a {@link EnumConstantNotPresentException} if the enum constant in the * annotation is no longer present in the enum type. * - *

Finally, Attempting to read a member whose definition has evolved + *

If an annotation type T is (meta-)annotated with an + * {@code @Repeatable} annotation whose value element indicates a type + * TC, but TC does not declare a {@code value()} method + * with a return type of T{@code []}, then an exception of type + * {@link java.lang.annotation.AnnotationFormatError} is thrown. + * + *

Finally, attempting to read a member whose definition has evolved * incompatibly will result in a {@link * java.lang.annotation.AnnotationTypeMismatchException} or an * {@link java.lang.annotation.IncompleteAnnotationException}. * * @see java.lang.EnumConstantNotPresentException * @see java.lang.TypeNotPresentException - * @see java.lang.annotation.AnnotationFormatError + * @see AnnotationFormatError * @see java.lang.annotation.AnnotationTypeMismatchException * @see java.lang.annotation.IncompleteAnnotationException * @since 1.5 @@ -61,9 +228,15 @@ public interface AnnotatedElement { /** * Returns true if an annotation for the specified type - * is present on this element, else false. This method + * is present on this element, else false. This method * is designed primarily for convenient access to marker annotations. * + *

The truth value returned by this method is equivalent to: + * {@code getAnnotation(annotationClass) != null} + * + *

The body of the default method is specified to be the code + * above. + * * @param annotationClass the Class object corresponding to the * annotation type * @return true if an annotation for the specified annotation @@ -71,12 +244,15 @@ public interface AnnotatedElement { * @throws NullPointerException if the given annotation class is null * @since 1.5 */ - boolean isAnnotationPresent(Class annotationClass); + default boolean isAnnotationPresent(Class annotationClass) { + return getAnnotation(annotationClass) != null; + } /** * Returns this element's annotation for the specified type if - * such an annotation is present, else null. + * such an annotation is present, else null. * + * @param the type of the annotation to query for and return if present * @param annotationClass the Class object corresponding to the * annotation type * @return this element's annotation for the specified annotation type if @@ -87,25 +263,185 @@ public interface AnnotatedElement { T getAnnotation(Class annotationClass); /** - * Returns all annotations present on this element. (Returns an array - * of length zero if this element has no annotations.) The caller of - * this method is free to modify the returned array; it will have no - * effect on the arrays returned to other callers. + * Returns annotations that are present on this element. * - * @return all annotations present on this element + * If there are no annotations present on this element, the return + * value is an array of length 0. + * + * The caller of this method is free to modify the returned array; it will + * have no effect on the arrays returned to other callers. + * + * @return annotations present on this element * @since 1.5 */ Annotation[] getAnnotations(); /** - * Returns all annotations that are directly present on this - * element. Unlike the other methods in this interface, this method - * ignores inherited annotations. (Returns an array of length zero if - * no annotations are directly present on this element.) The caller of - * this method is free to modify the returned array; it will have no - * effect on the arrays returned to other callers. - * - * @return All annotations directly present on this element + * Returns annotations that are associated with this element. + * + * If there are no annotations associated with this element, the return + * value is an array of length 0. + * + * The difference between this method and {@link #getAnnotation(Class)} + * is that this method detects if its argument is a repeatable + * annotation type (JLS 9.6), and if so, attempts to find one or + * more annotations of that type by "looking through" a container + * annotation. + * + * The caller of this method is free to modify the returned array; it will + * have no effect on the arrays returned to other callers. + * + * @implSpec The default implementation first calls {@link + * #getDeclaredAnnotationsByType(Class)} passing {@code + * annotationClass} as the argument. If the returned array has + * length greater than zero, the array is returned. If the returned + * array is zero-length and this {@code AnnotatedElement} is a + * class and the argument type is an inheritable annotation type, + * and the superclass of this {@code AnnotatedElement} is non-null, + * then the returned result is the result of calling {@link + * #getAnnotationsByType(Class)} on the superclass with {@code + * annotationClass} as the argument. Otherwise, a zero-length + * array is returned. + * + * @param the type of the annotation to query for and return if present + * @param annotationClass the Class object corresponding to the + * annotation type + * @return all this element's annotations for the specified annotation type if + * associated with this element, else an array of length zero + * @throws NullPointerException if the given annotation class is null + * @since 1.8 + */ + default T[] getAnnotationsByType(Class annotationClass) { + /* + * Definition of associated: directly or indirectly present OR + * neither directly nor indirectly present AND the element is + * a Class, the annotation type is inheritable, and the + * annotation type is associated with the superclass of the + * element. + */ + T[] result = getDeclaredAnnotationsByType(annotationClass); + + Class Inherited; + try { + Inherited = Class.forName("java.lang.annotation.Inherited").asSubclass(Annotation.class); + } catch (ClassNotFoundException ex) { + Inherited = Annotation.class; + } + + if (result.length == 0 && // Neither directly nor indirectly present + this instanceof Class && // the element is a class + annotationClass.getAnnotation(Inherited) != null) { // Inheritable + Class superClass = ((Class) this).getSuperclass(); + if (superClass != null) { + // Determine if the annotation is associated with the + // superclass + result = superClass.getAnnotationsByType(annotationClass); + } + } + + return result; + } + + /** + * Returns this element's annotation for the specified type if + * such an annotation is directly present, else null. + * + * This method ignores inherited annotations. (Returns null if no + * annotations are directly present on this element.) + * + * @implSpec The default implementation first performs a null check + * and then loops over the results of {@link + * #getDeclaredAnnotations} returning the first annotation whose + * annotation type matches the argument type. + * + * @param the type of the annotation to query for and return if directly present + * @param annotationClass the Class object corresponding to the + * annotation type + * @return this element's annotation for the specified annotation type if + * directly present on this element, else null + * @throws NullPointerException if the given annotation class is null + * @since 1.8 + */ + default T getDeclaredAnnotation(Class annotationClass) { + annotationClass.getClass(); + // Loop over all directly-present annotations looking for a matching one + for (Annotation annotation : getDeclaredAnnotations()) { + if (annotationClass.equals(annotation.annotationType())) { + // More robust to do a dynamic cast at runtime instead + // of compile-time only. + return annotationClass.cast(annotation); + } + } + return null; + } + + /** + * Returns this element's annotation(s) for the specified type if + * such annotations are either directly present or + * indirectly present. This method ignores inherited + * annotations. + * + * If there are no specified annotations directly or indirectly + * present on this element, the return value is an array of length + * 0. + * + * The difference between this method and {@link + * #getDeclaredAnnotation(Class)} is that this method detects if its + * argument is a repeatable annotation type (JLS 9.6), and if so, + * attempts to find one or more annotations of that type by "looking + * through" a container annotation if one is present. + * + * The caller of this method is free to modify the returned array; it will + * have no effect on the arrays returned to other callers. + * + * @implSpec The default implementation may call {@link + * #getDeclaredAnnotation(Class)} one or more times to find a + * directly present annotation and, if the annotation type is + * repeatable, to find a container annotation. If annotations of + * the annotation type {@code annotationClass} are found to be both + * directly and indirectly present, then {@link + * #getDeclaredAnnotations()} will get called to determine the + * order of the elements in the returned array. + * + *

Alternatively, the default implementation may call {@link + * #getDeclaredAnnotations()} a single time and the returned array + * examined for both directly and indirectly present + * annotations. The results of calling {@link + * #getDeclaredAnnotations()} are assumed to be consistent with the + * results of calling {@link #getDeclaredAnnotation(Class)}. + * + * @param the type of the annotation to query for and return + * if directly or indirectly present + * @param annotationClass the Class object corresponding to the + * annotation type + * @return all this element's annotations for the specified annotation type if + * directly or indirectly present on this element, else an array of length zero + * @throws NullPointerException if the given annotation class is null + * @since 1.8 + */ + default T[] getDeclaredAnnotationsByType(Class annotationClass) { + annotationClass.getClass(); +// return AnnotationSupport. +// getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()). +// collect(Collectors.toMap(Annotation::annotationType, +// Function.identity(), +// ((first,second) -> first), +// LinkedHashMap::new)), +// annotationClass); + return (T[])new Annotation[0]; + } + + /** + * Returns annotations that are directly present on this element. + * This method ignores inherited annotations. + * + * If there are no annotations directly present on this element, + * the return value is an array of length 0. + * + * The caller of this method is free to modify the returned array; it will + * have no effect on the arrays returned to other callers. + * + * @return annotations directly present on this element * @since 1.5 */ Annotation[] getDeclaredAnnotations(); diff --git a/rt/vm/pom.xml b/rt/vm/pom.xml index a24cdc54d..d8a3f336a 100644 --- a/rt/vm/pom.xml +++ b/rt/vm/pom.xml @@ -67,7 +67,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin @@ -119,4 +119,26 @@ test + + + target15 + + + + org.apache.maven.plugins + maven-compiler-plugin + + javac + 8 + 1.5 + 1.5 + + -Xlint:deprecation + + + + + + + diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/AbstractStackMapper.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/AbstractStackMapper.java new file mode 100644 index 000000000..6b77edad1 --- /dev/null +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/AbstractStackMapper.java @@ -0,0 +1,128 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import org.apidesign.vm4brwsr.ByteCodeParser.TypeArray; + +abstract class AbstractStackMapper { + public abstract void clear(); + + public abstract void syncWithFrameStack(final TypeArray frameStack); + + public final CharSequence pushI() { + return pushT(VarType.INTEGER); + } + + public final CharSequence pushL() { + return pushT(VarType.LONG); + } + + public final CharSequence pushF() { + return pushT(VarType.FLOAT); + } + + public final CharSequence pushD() { + return pushT(VarType.DOUBLE); + } + + public final CharSequence pushA() { + return pushT(VarType.REFERENCE); + } + + public abstract CharSequence pushT(final int type); + + abstract void assign(Appendable out, int varType, CharSequence s) throws IOException; + + abstract void replace(Appendable out, int varType, String format, CharSequence... arr) + throws IOException; + + abstract void flush(Appendable out) throws IOException; + + public abstract boolean isDirty(); + + public final CharSequence popI(Appendable out) throws IOException { + return popT(out, VarType.INTEGER); + } + + public final CharSequence popL(Appendable out) throws IOException { + return popT(out, VarType.LONG); + } + + public final CharSequence popF(Appendable out) throws IOException { + return popT(out, VarType.FLOAT); + } + + public final CharSequence popD(Appendable out) throws IOException { + return popT(out, VarType.DOUBLE); + } + + public final CharSequence popA(Appendable out) throws IOException { + return popT(out, VarType.REFERENCE); + } + + public abstract CharSequence popT(Appendable out, final int type) throws IOException; + + public abstract CharSequence popValue(Appendable out) throws IOException; + + public abstract Variable pop(Appendable out) throws IOException; + + public abstract void pop(Appendable out, final int count) throws IOException; + + public final CharSequence getI(Appendable out, final int indexFromTop) throws IOException { + return getT(out, indexFromTop, VarType.INTEGER); + } + + public final CharSequence getL(Appendable out, final int indexFromTop) throws IOException { + return getT(out, indexFromTop, VarType.LONG); + } + + public final CharSequence getF(Appendable out, final int indexFromTop) throws IOException { + return getT(out, indexFromTop, VarType.FLOAT); + } + + public final CharSequence getD(Appendable out, final int indexFromTop) throws IOException { + return getT(out, indexFromTop, VarType.DOUBLE); + } + + public final CharSequence getA(Appendable out, final int indexFromTop) throws IOException { + return getT(out, indexFromTop, VarType.REFERENCE); + } + + public final CharSequence getT(Appendable out, final int indexFromTop, final int type) throws IOException { + return getT(out, indexFromTop, type, true); + } + public abstract CharSequence getT(Appendable out, final int indexFromTop, final int type, boolean clear) throws IOException; + + public abstract Variable get(Appendable out, final int indexFromTop) throws IOException; + + public static Variable getVariable(final int typeAndIndex) { + final int type = typeAndIndex & 0xff; + final int index = typeAndIndex >> 8; + + return Variable.getStackVariable(type, index); + } + + abstract boolean alwaysUseGt(); + + abstract int initCode(Appendable out) throws IOException; + + abstract void finishStatement(Appendable out) throws IOException; + + abstract void caughtException(Appendable out, String e) throws IOException; +} diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java index e5324ba10..5412214b2 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java @@ -406,6 +406,13 @@ private String compileImpl(Appendable out, final String cn) throws IOException { return ""; } + private boolean versionCheck() throws IOException { + if (jc.getMajor_version() < 50) { + return false; + } + return true; + } + private StringArray findJavaScriptResources(byte[] arr, final String cn) throws IOException { if (arr == null) { return null; @@ -498,25 +505,18 @@ private boolean generateMethod(Appendable out, String destObject, String name, M out.append(") {").append("\n"); final byte[] byteCodes = m.getCode(); - if (byteCodes == null || jc.getMajor_version() < 50) { - if (byteCodes == null) { - byte[] defaultAttr = m.getDefaultAttribute(); - if (defaultAttr != null) { - out.append(" return "); - AnnotationParser ap = new GenerateAnno(out, true, false); - ap.parseDefault(defaultAttr, jc); - out.append(";\n"); - } else { - if (debug(out, " throw 'no code found for ")) { - out.append(jc.getClassName()).append('.') - .append(m.getName()).append("';\n"); - } - } + if (byteCodes == null) { + byte[] defaultAttr = m.getDefaultAttribute(); + if (defaultAttr != null) { + out.append(" return "); + AnnotationParser ap = new GenerateAnno(out, true, false); + ap.parseDefault(defaultAttr, jc); + out.append(";\n"); } else { - out.append(" throw 'Class file version for " + jc.getClassName() + " is " + jc.getMajor_version() + "." - + jc.getMinor_version() + " - recompile with -target 1.6 (at least)';\n" - ); - + if (debug(out, " throw 'no code found for ")) { + out.append(jc.getClassName()).append('.') + .append(m.getName()).append("';\n"); + } } if (defineProp) { out.append("}});"); @@ -526,7 +526,12 @@ private boolean generateMethod(Appendable out, String destObject, String name, M return defineProp; } - final StackMapper smapper = new StackMapper(); + final AbstractStackMapper smapper; + if (versionCheck()) { + smapper = new StackMapper(); + } else { + smapper = new OldStackMapper(); + } if (!m.isStatic()) { out.append(" var ").append(" lcA0 = this;\n"); @@ -550,7 +555,7 @@ private boolean generateMethod(Appendable out, String destObject, String name, M return defineProp; } - static int generateIf(Appendable out, StackMapper mapper, byte[] byteCodes, + static int generateIf(Appendable out, AbstractStackMapper mapper, byte[] byteCodes, int i, final CharSequence v2, final CharSequence v1, final String test, int topMostLabel ) throws IOException { @@ -559,7 +564,7 @@ static int generateIf(Appendable out, StackMapper mapper, byte[] byteCodes, out.append("if ((").append(v1) .append(") ").append(test).append(" (") .append(v2).append(")) "); - goTo(out, i, indx, topMostLabel); + goTo(out, i, indx, topMostLabel, mapper.alwaysUseGt()); return i + 2; } @@ -1108,13 +1113,13 @@ private static String outputArg(Appendable out, String[] args, int indx) throws } final void emitNoFlush( - Appendable out, StackMapper sm, + Appendable out, AbstractStackMapper sm, final String format, final CharSequence... params ) throws IOException { emitImpl(out, format, params); } static final void emit( - final Appendable out, StackMapper sm, final String format, final CharSequence... params + final Appendable out, AbstractStackMapper sm, final String format, final CharSequence... params ) throws IOException { sm.flush(out); emitImpl(out, format, params); @@ -1144,8 +1149,9 @@ static void emitImpl(final Appendable out, out.append(format, processed, length); } - void generateCatch(Appendable out, TrapData[] traps, int current, int topMostLabel) throws IOException { + void generateCatch(Appendable out, AbstractStackMapper mapper, TrapData[] traps, int current, int topMostLabel, boolean useGt) throws IOException { out.append("} catch (e) {\n"); + out.append(" e = vm.java_lang_Class(false).bck2BrwsrThrwrbl(e);\n"); int finallyPC = -1; for (TrapData e : traps) { if (e == null) { @@ -1154,11 +1160,10 @@ void generateCatch(Appendable out, TrapData[] traps, int current, int topMostLab if (e.catch_cpx != 0) { //not finally final String classInternalName = jc.getClassName(e.catch_cpx); addReference(out, classInternalName); - out.append("e = vm.java_lang_Class(false).bck2BrwsrThrwrbl(e);"); - out.append("if (e['$instOf_" + mangleClassName(classInternalName) + "']) {"); - out.append("var stA0 = e;"); - goTo(out, current, e.handler_pc, topMostLabel); - out.append("}\n"); + out.append(" if (e['$instOf_" + mangleClassName(classInternalName) + "']) {\n"); + mapper.caughtException(out, "e"); + goTo(out, current, e.handler_pc, topMostLabel, useGt); + out.append(" }\n"); } else { finallyPC = e.handler_pc; } @@ -1166,13 +1171,18 @@ void generateCatch(Appendable out, TrapData[] traps, int current, int topMostLab if (finallyPC == -1) { out.append("throw e;"); } else { - out.append("var stA0 = e;"); - goTo(out, current, finallyPC, topMostLabel); + mapper.caughtException(out, "e"); + goTo(out, current, finallyPC, topMostLabel, useGt); } out.append("\n}"); } - static void goTo(Appendable out, int current, int to, int canBack) throws IOException { + static void goTo(Appendable out, int current, int to, int canBack, boolean useGt) throws IOException { + if (useGt) { + out.append("{ gt = " + to + "; continue X_0; }"); + return; + } + if (to < current) { if (canBack < to) { out.append("{ gt = 0; continue X_" + to + "; }"); @@ -1185,14 +1195,14 @@ static void goTo(Appendable out, int current, int to, int canBack) throws IOExce } static void emitIf( - Appendable out, StackMapper sm, String pattern, CharSequence param, int current, int to, int canBack + Appendable out, AbstractStackMapper sm, String pattern, CharSequence param, int current, int to, int canBack ) throws IOException { sm.flush(out); emitImpl(out, pattern, param); - goTo(out, current, to, canBack); + goTo(out, current, to, canBack, sm.alwaysUseGt()); } - void generateNewArray(Appendable out, int atype, final StackMapper smapper) throws IOException, IllegalStateException { + void generateNewArray(Appendable out, int atype, final AbstractStackMapper smapper) throws IOException, IllegalStateException { String jvmType; switch (atype) { case 4: jvmType = "[Z"; break; @@ -1207,10 +1217,10 @@ void generateNewArray(Appendable out, int atype, final StackMapper smapper) thro } emit(out, smapper, "var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](true, '@3', null, @1);", - smapper.popI(), smapper.pushA(), jvmType); + smapper.popI(out), smapper.pushA(), jvmType); } - void generateANewArray(Appendable out, int type, final StackMapper smapper) throws IOException { + void generateANewArray(Appendable out, int type, final AbstractStackMapper smapper) throws IOException { String typeName = jc.getClassName(type); String ref = "null"; if (typeName.startsWith("[")) { @@ -1221,10 +1231,10 @@ void generateANewArray(Appendable out, int type, final StackMapper smapper) thro } emit(out, smapper, "var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](false, @3, @4, @1);", - smapper.popI(), smapper.pushA(), typeName, ref); + smapper.popI(out), smapper.pushA(), typeName, ref); } - int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int i, final StackMapper smapper) throws IOException { + int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int i, final AbstractStackMapper smapper) throws IOException { String typeName = jc.getClassName(type); int dim = readUByte(byteCodes, ++i); StringBuilder dims = new StringBuilder(); @@ -1233,7 +1243,7 @@ int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int if (d != 0) { dims.insert(1, ","); } - dims.insert(1, smapper.popI()); + dims.insert(1, smapper.popI(out)); } dims.append(']'); String fn = "null"; @@ -1247,7 +1257,7 @@ int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int return i; } - int generateTableSwitch(Appendable out, int i, final byte[] byteCodes, final StackMapper smapper, int topMostLabel) throws IOException { + int generateTableSwitch(Appendable out, int i, final byte[] byteCodes, final AbstractStackMapper smapper, int topMostLabel) throws IOException { int table = i / 4 * 4 + 4; int dflt = i + readInt4(byteCodes, table); table += 4; @@ -1255,29 +1265,29 @@ int generateTableSwitch(Appendable out, int i, final byte[] byteCodes, final Sta table += 4; int high = readInt4(byteCodes, table); table += 4; - final CharSequence swVar = smapper.popValue(); + final CharSequence swVar = smapper.popValue(out); smapper.flush(out); out.append("switch (").append(swVar).append(") {\n"); while (low <= high) { int offset = i + readInt4(byteCodes, table); table += 4; - out.append(" case " + low).append(":"); goTo(out, i, offset, topMostLabel); out.append('\n'); + out.append(" case " + low).append(":"); goTo(out, i, offset, topMostLabel, smapper.alwaysUseGt()); out.append('\n'); low++; } out.append(" default: "); - goTo(out, i, dflt, topMostLabel); + goTo(out, i, dflt, topMostLabel, smapper.alwaysUseGt()); out.append("\n}"); i = table - 1; return i; } - int generateLookupSwitch(Appendable out, int i, final byte[] byteCodes, final StackMapper smapper, int topMostLabel) throws IOException { + int generateLookupSwitch(Appendable out, int i, final byte[] byteCodes, final AbstractStackMapper smapper, int topMostLabel) throws IOException { int table = i / 4 * 4 + 4; int dflt = i + readInt4(byteCodes, table); table += 4; int n = readInt4(byteCodes, table); table += 4; - final CharSequence swVar = smapper.popValue(); + final CharSequence swVar = smapper.popValue(out); smapper.flush(out); out.append("switch (").append(swVar).append(") {\n"); while (n-- > 0) { @@ -1285,21 +1295,21 @@ int generateLookupSwitch(Appendable out, int i, final byte[] byteCodes, final St table += 4; int offset = i + readInt4(byteCodes, table); table += 4; - out.append(" case " + cnstnt).append(": "); goTo(out, i, offset, topMostLabel); out.append('\n'); + out.append(" case " + cnstnt).append(": "); goTo(out, i, offset, topMostLabel, smapper.alwaysUseGt()); out.append('\n'); } out.append(" default: "); - goTo(out, i, dflt, topMostLabel); + goTo(out, i, dflt, topMostLabel, smapper.alwaysUseGt()); out.append("\n}"); i = table - 1; return i; } - void generateInstanceOf(Appendable out, int indx, final StackMapper smapper) throws IOException { + void generateInstanceOf(Appendable out, int indx, final AbstractStackMapper smapper) throws IOException { String type = jc.getClassName(indx); if (!type.startsWith("[")) { emit(out, smapper, "var @2 = @1 != null && @1['$instOf_@3'] ? 1 : 0;", - smapper.popA(), smapper.pushI(), + smapper.popA(out), smapper.pushI(), mangleClassName(type)); } else { int cnt = 0; @@ -1312,24 +1322,24 @@ void generateInstanceOf(Appendable out, int indx, final StackMapper smapper) thr type = "vm." + mangleClassName(component); emit(out, smapper, "var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @4, @3);", - smapper.popA(), smapper.pushI(), + smapper.popA(out), smapper.pushI(), type, "" + cnt ); } else { emit(out, smapper, "var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@3');", - smapper.popA(), smapper.pushI(), type + smapper.popA(out), smapper.pushI(), type ); } } } - void generateCheckcast(Appendable out, int indx, final StackMapper smapper) throws IOException { + void generateCheckcast(Appendable out, int indx, final AbstractStackMapper smapper) throws IOException { String type = jc.getClassName(indx); if (!type.startsWith("[")) { emitNoFlush(out, smapper, "if (@1 !== null && !@1['$instOf_@2']) vm.java_lang_Class(false).castEx(@1, '@3');", - smapper.getT(0, VarType.REFERENCE, false), mangleClassName(type), type.replace('/', '.')); + smapper.getT(out, 0, VarType.REFERENCE, false), mangleClassName(type), type.replace('/', '.')); } else { int cnt = 0; while (type.charAt(cnt) == '[') { @@ -1341,12 +1351,12 @@ void generateCheckcast(Appendable out, int indx, final StackMapper smapper) thro type = "vm." + mangleClassName(component); emitNoFlush(out, smapper, "if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @3, @2)) vm.java_lang_Class(false).castEx(@1, '');", - smapper.getT(0, VarType.REFERENCE, false), type, "" + cnt + smapper.getT(out, 0, VarType.REFERENCE, false), type, "" + cnt ); } else { emitNoFlush(out, smapper, "if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@2')) vm.java_lang_Class(false).castEx(@1, '');", - smapper.getT(0, VarType.REFERENCE, false), type + smapper.getT(out, 0, VarType.REFERENCE, false), type ); } } diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java index 4486ab964..28cc0e137 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java @@ -1,217 +1,222 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012-2018 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -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 { - private static final Object MISSING_CLASS = new Object(); - - private final Bck2Brwsr.Resources resources; - private final Map classDataMap; - - ClassDataCache(final Bck2Brwsr.Resources resources) { - this.resources = resources; - - classDataMap = new HashMap(); - } - - 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); - try { - cacheEntry = (is != null) ? new ClassData(is) : MISSING_CLASS; - } catch (IOException ex) { - throw new IOException("Cannot process " + className, ex); - } - classDataMap.put(className, cacheEntry); - } - - 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(final ClassData startingClass, - final String name, - final String signature) throws IOException { - final FindFirstTraversalCallback ffTraversalCallback = - new FindFirstTraversalCallback(); - - findMethods(startingClass, name, signature, ffTraversalCallback); - return ffTraversalCallback.getFirst(); - } - - FieldData findField(final ClassData startingClass, - final String name, - final String signature) throws IOException { - final FindFirstTraversalCallback ffTraversalCallback = - new FindFirstTraversalCallback(); - - findFields(startingClass, name, signature, ffTraversalCallback); - return ffTraversalCallback.getFirst(); - } - - void findMethods(final ClassData startingClass, - final String methodName, - final String methodSignature, - final TraversalCallback mdTraversalCallback) - throws IOException { - traverseHierarchy( - startingClass, - new FindMethodsTraversalCallback(methodName, methodSignature, - mdTraversalCallback)); - } - - void findFields(final ClassData startingClass, - final String fieldName, - final String fieldSignature, - final TraversalCallback fdTraversalCallback) - throws IOException { - traverseHierarchy( - startingClass, - new FindFieldsTraversalCallback(fieldName, fieldSignature, - fdTraversalCallback)); - } - - private boolean traverseHierarchy( - ClassData currentClass, - final TraversalCallback cdTraversalCallback) - throws IOException { - while (currentClass != null) { - if (!cdTraversalCallback.traverse(currentClass)) { - return false; - } - - for (final String superIfaceName: - currentClass.getSuperInterfaces()) { - if (!traverseHierarchy(getClassData(superIfaceName), - cdTraversalCallback)) { - return false; - } - } - - final String superClassName = currentClass.getSuperClassName(); - if (superClassName == null) { - break; - } - - currentClass = getClassData(superClassName); - } - - return true; - } - - interface TraversalCallback { - boolean traverse(T object); - } - - private final class FindFirstTraversalCallback - implements TraversalCallback { - private T firstObject; - - @Override - public boolean traverse(final T object) { - firstObject = object; - return false; - } - - public T getFirst() { - return firstObject; - } - } - - private final class FindMethodsTraversalCallback - implements TraversalCallback { - private final String methodName; - private final String methodSignature; - private final TraversalCallback mdTraversalCallback; - - public FindMethodsTraversalCallback( - final String methodName, - final String methodSignature, - final TraversalCallback mdTraversalCallback) { - this.methodName = methodName; - this.methodSignature = methodSignature; - this.mdTraversalCallback = mdTraversalCallback; - } - - @Override - public boolean traverse(final ClassData classData) { - final MethodData methodData = - classData.findMethod(methodName, methodSignature); - return (methodData != null) - ? mdTraversalCallback.traverse(methodData) - : true; - } - } - - private final class FindFieldsTraversalCallback - implements TraversalCallback { - private final String fieldName; - private final String fieldSignature; - private final TraversalCallback fdTraversalCallback; - - public FindFieldsTraversalCallback( - final String fieldName, - final String fieldSignature, - final TraversalCallback fdTraversalCallback) { - this.fieldName = fieldName; - this.fieldSignature = fieldSignature; - this.fdTraversalCallback = fdTraversalCallback; - } - - @Override - public boolean traverse(final ClassData classData) { - final FieldData fieldData = - classData.findField(fieldName, fieldSignature); - return (fieldData != null) - ? fdTraversalCallback.traverse(fieldData) - : true; - } - } - - private static InputStream loadClass(Bck2Brwsr.Resources l, String name) - throws IOException { - return l.get(name + ".class"); // NOI18N - } -} +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +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 { + private static final Object MISSING_CLASS = new Object(); + + private final Bck2Brwsr.Resources resources; + private final Map classDataMap; + + ClassDataCache(final Bck2Brwsr.Resources resources) { + this.resources = resources; + + classDataMap = new HashMap(); + } + + 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) { + InputStream is = null; + try { + is = loadClass(resources, className); + } catch (IOException loadOrNot) { + is = null; + } + try { + cacheEntry = (is != null) ? new ClassData(is) : MISSING_CLASS; + } catch (IOException ex) { + throw new IOException("Cannot process " + className, ex); + } + classDataMap.put(className, cacheEntry); + } + + 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(final ClassData startingClass, + final String name, + final String signature) throws IOException { + final FindFirstTraversalCallback ffTraversalCallback = + new FindFirstTraversalCallback(); + + findMethods(startingClass, name, signature, ffTraversalCallback); + return ffTraversalCallback.getFirst(); + } + + FieldData findField(final ClassData startingClass, + final String name, + final String signature) throws IOException { + final FindFirstTraversalCallback ffTraversalCallback = + new FindFirstTraversalCallback(); + + findFields(startingClass, name, signature, ffTraversalCallback); + return ffTraversalCallback.getFirst(); + } + + void findMethods(final ClassData startingClass, + final String methodName, + final String methodSignature, + final TraversalCallback mdTraversalCallback) + throws IOException { + traverseHierarchy( + startingClass, + new FindMethodsTraversalCallback(methodName, methodSignature, + mdTraversalCallback)); + } + + void findFields(final ClassData startingClass, + final String fieldName, + final String fieldSignature, + final TraversalCallback fdTraversalCallback) + throws IOException { + traverseHierarchy( + startingClass, + new FindFieldsTraversalCallback(fieldName, fieldSignature, + fdTraversalCallback)); + } + + private boolean traverseHierarchy( + ClassData currentClass, + final TraversalCallback cdTraversalCallback) + throws IOException { + while (currentClass != null) { + if (!cdTraversalCallback.traverse(currentClass)) { + return false; + } + + for (final String superIfaceName: + currentClass.getSuperInterfaces()) { + if (!traverseHierarchy(getClassData(superIfaceName), + cdTraversalCallback)) { + return false; + } + } + + final String superClassName = currentClass.getSuperClassName(); + if (superClassName == null) { + break; + } + + currentClass = getClassData(superClassName); + } + + return true; + } + + interface TraversalCallback { + boolean traverse(T object); + } + + private final class FindFirstTraversalCallback + implements TraversalCallback { + private T firstObject; + + @Override + public boolean traverse(final T object) { + firstObject = object; + return false; + } + + public T getFirst() { + return firstObject; + } + } + + private final class FindMethodsTraversalCallback + implements TraversalCallback { + private final String methodName; + private final String methodSignature; + private final TraversalCallback mdTraversalCallback; + + public FindMethodsTraversalCallback( + final String methodName, + final String methodSignature, + final TraversalCallback mdTraversalCallback) { + this.methodName = methodName; + this.methodSignature = methodSignature; + this.mdTraversalCallback = mdTraversalCallback; + } + + @Override + public boolean traverse(final ClassData classData) { + final MethodData methodData = + classData.findMethod(methodName, methodSignature); + return (methodData != null) + ? mdTraversalCallback.traverse(methodData) + : true; + } + } + + private final class FindFieldsTraversalCallback + implements TraversalCallback { + private final String fieldName; + private final String fieldSignature; + private final TraversalCallback fdTraversalCallback; + + public FindFieldsTraversalCallback( + final String fieldName, + final String fieldSignature, + final TraversalCallback fdTraversalCallback) { + this.fieldName = fieldName; + this.fieldSignature = fieldSignature; + this.fdTraversalCallback = fdTraversalCallback; + } + + @Override + public boolean traverse(final ClassData classData) { + final FieldData fieldData = + classData.findField(fieldName, fieldSignature); + return (fieldData != null) + ? fdTraversalCallback.traverse(fieldData) + : true; + } + } + + private static InputStream loadClass(Bck2Brwsr.Resources l, String name) + throws IOException { + return l.get(name + ".class"); // NOI18N + } +} diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/IndyHandler.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/IndyHandler.java index 4d46658d7..10df45a10 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/IndyHandler.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/IndyHandler.java @@ -32,12 +32,12 @@ abstract class IndyHandler { static class Ctx { final Appendable out; - final StackMapper stackMapper; + final AbstractStackMapper stackMapper; final ByteCodeToJavaScript byteCodeToJavaScript; final ByteCodeParser.BootMethodData bm; final String[] mt; - Ctx(Appendable out, StackMapper m, ByteCodeToJavaScript bc, String[] methodAndType, ByteCodeParser.BootMethodData bm) { + Ctx(Appendable out, AbstractStackMapper m, ByteCodeToJavaScript bc, String[] methodAndType, ByteCodeParser.BootMethodData bm) { this.out = out; this.stackMapper = m; this.byteCodeToJavaScript = bc; diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/LoopCode.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/LoopCode.java index 4f34043ab..43dcd6e2c 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/LoopCode.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/LoopCode.java @@ -42,7 +42,7 @@ public void run() { void loopCode( final ByteCodeParser.StackMapIterator stackMapIterator, final byte[] byteCodes, ByteCodeParser.TrapDataIterator trap, - final StackMapper smapper, + final AbstractStackMapper smapper, final LocalsMapper lmapper ) throws IllegalStateException, NumberFormatException, IOException { int lastStackFrame; @@ -57,7 +57,7 @@ void loopCode( lastStackFrame = -1; out.append("\n var gt = 0;\n"); } - int openBraces = 0; + int openBraces = smapper.initCode(out); int topMostLabel = 0; for (int i = 0; i < byteCodes.length; i++) { int prev = i; @@ -66,11 +66,13 @@ void loopCode( boolean changeInCatch = trap.advanceTo(i); if (changeInCatch || lastStackFrame != stackMapIterator.getFrameIndex()) { if (previousTrap != null) { - byteCodeToJavaScript.generateCatch(out, previousTrap, i, topMostLabel); + byteCodeToJavaScript.generateCatch(out, smapper, previousTrap, i, topMostLabel, smapper.alwaysUseGt()); previousTrap = null; } } - if (lastStackFrame != stackMapIterator.getFrameIndex()) { + if (smapper.alwaysUseGt()) { + // no op + } else if (lastStackFrame != stackMapIterator.getFrameIndex()) { smapper.flush(out); if (i != 0) { out.append(" }\n"); @@ -95,6 +97,9 @@ void loopCode( out.append("try {"); previousTrap = trap.current(); } + if (smapper.alwaysUseGt()) { + out.append(" if (gt <= " + i + ") {\n"); + } final int c = ByteCodeToJavaScript.readUByte(byteCodes, i); switch (c) { case ByteCodeParser.opc_nop: @@ -204,7 +209,7 @@ void loopCode( ++i; final int indx = wide ? ByteCodeToJavaScript.readUShort(byteCodes, i++) : ByteCodeToJavaScript.readUByte(byteCodes, i); wide = false; - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(indx), smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(indx), smapper.popI(out)); break; } case ByteCodeParser.opc_lstore: @@ -212,7 +217,7 @@ void loopCode( ++i; final int indx = wide ? ByteCodeToJavaScript.readUShort(byteCodes, i++) : ByteCodeToJavaScript.readUByte(byteCodes, i); wide = false; - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(indx), smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(indx), smapper.popL(out)); break; } case ByteCodeParser.opc_fstore: @@ -220,7 +225,7 @@ void loopCode( ++i; final int indx = wide ? ByteCodeToJavaScript.readUShort(byteCodes, i++) : ByteCodeToJavaScript.readUByte(byteCodes, i); wide = false; - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(indx), smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(indx), smapper.popF(out)); break; } case ByteCodeParser.opc_dstore: @@ -228,7 +233,7 @@ void loopCode( ++i; final int indx = wide ? ByteCodeToJavaScript.readUShort(byteCodes, i++) : ByteCodeToJavaScript.readUByte(byteCodes, i); wide = false; - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(indx), smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(indx), smapper.popD(out)); break; } case ByteCodeParser.opc_astore: @@ -236,176 +241,176 @@ void loopCode( ++i; final int indx = wide ? ByteCodeToJavaScript.readUShort(byteCodes, i++) : ByteCodeToJavaScript.readUByte(byteCodes, i); wide = false; - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(indx), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(indx), smapper.popA(out)); break; } case ByteCodeParser.opc_astore_0: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(0), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(0), smapper.popA(out)); break; case ByteCodeParser.opc_istore_0: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(0), smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(0), smapper.popI(out)); break; case ByteCodeParser.opc_lstore_0: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(0), smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(0), smapper.popL(out)); break; case ByteCodeParser.opc_fstore_0: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(0), smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(0), smapper.popF(out)); break; case ByteCodeParser.opc_dstore_0: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(0), smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(0), smapper.popD(out)); break; case ByteCodeParser.opc_astore_1: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(1), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(1), smapper.popA(out)); break; case ByteCodeParser.opc_istore_1: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(1), smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(1), smapper.popI(out)); break; case ByteCodeParser.opc_lstore_1: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(1), smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(1), smapper.popL(out)); break; case ByteCodeParser.opc_fstore_1: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(1), smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(1), smapper.popF(out)); break; case ByteCodeParser.opc_dstore_1: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(1), smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(1), smapper.popD(out)); break; case ByteCodeParser.opc_astore_2: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(2), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(2), smapper.popA(out)); break; case ByteCodeParser.opc_istore_2: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(2), smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(2), smapper.popI(out)); break; case ByteCodeParser.opc_lstore_2: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(2), smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(2), smapper.popL(out)); break; case ByteCodeParser.opc_fstore_2: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(2), smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(2), smapper.popF(out)); break; case ByteCodeParser.opc_dstore_2: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(2), smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(2), smapper.popD(out)); break; case ByteCodeParser.opc_astore_3: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(3), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setA(3), smapper.popA(out)); break; case ByteCodeParser.opc_istore_3: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(3), smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setI(3), smapper.popI(out)); break; case ByteCodeParser.opc_lstore_3: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(3), smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setL(3), smapper.popL(out)); break; case ByteCodeParser.opc_fstore_3: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(3), smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setF(3), smapper.popF(out)); break; case ByteCodeParser.opc_dstore_3: - ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(3), smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", lmapper.setD(3), smapper.popD(out)); break; case ByteCodeParser.opc_iadd: - smapper.replace(out, VarType.INTEGER, "(((@1) + (@2)) | 0)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(((@1) + (@2)) | 0)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_ladd: - smapper.replace(out, VarType.LONG, numbers.add64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.add64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_fadd: - smapper.replace(out, VarType.FLOAT, "(@1 + @2)", smapper.getF(1), smapper.popF()); + smapper.replace(out, VarType.FLOAT, "(@1 + @2)", smapper.getF(out, 1), smapper.popF(out)); break; case ByteCodeParser.opc_dadd: - smapper.replace(out, VarType.DOUBLE, "(@1 + @2)", smapper.getD(1), smapper.popD()); + smapper.replace(out, VarType.DOUBLE, "(@1 + @2)", smapper.getD(out, 1), smapper.popD(out)); break; case ByteCodeParser.opc_isub: - smapper.replace(out, VarType.INTEGER, "(((@1) - (@2)) | 0)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(((@1) - (@2)) | 0)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lsub: - smapper.replace(out, VarType.LONG, numbers.sub64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.sub64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_fsub: - smapper.replace(out, VarType.FLOAT, "(@1 - @2)", smapper.getF(1), smapper.popF()); + smapper.replace(out, VarType.FLOAT, "(@1 - @2)", smapper.getF(out, 1), smapper.popF(out)); break; case ByteCodeParser.opc_dsub: - smapper.replace(out, VarType.DOUBLE, "(@1 - @2)", smapper.getD(1), smapper.popD()); + smapper.replace(out, VarType.DOUBLE, "(@1 - @2)", smapper.getD(out, 1), smapper.popD(out)); break; case ByteCodeParser.opc_imul: - smapper.replace(out, VarType.INTEGER, numbers.mul32(), smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, numbers.mul32(), smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lmul: - smapper.replace(out, VarType.LONG, numbers.mul64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.mul64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_fmul: - smapper.replace(out, VarType.FLOAT, "(@1 * @2)", smapper.getF(1), smapper.popF()); + smapper.replace(out, VarType.FLOAT, "(@1 * @2)", smapper.getF(out, 1), smapper.popF(out)); break; case ByteCodeParser.opc_dmul: - smapper.replace(out, VarType.DOUBLE, "(@1 * @2)", smapper.getD(1), smapper.popD()); + smapper.replace(out, VarType.DOUBLE, "(@1 * @2)", smapper.getD(out, 1), smapper.popD(out)); break; case ByteCodeParser.opc_idiv: - smapper.replace(out, VarType.INTEGER, numbers.div32(), smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, numbers.div32(), smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_ldiv: - smapper.replace(out, VarType.LONG, numbers.div64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.div64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_fdiv: - smapper.replace(out, VarType.FLOAT, "(@1 / @2)", smapper.getF(1), smapper.popF()); + smapper.replace(out, VarType.FLOAT, "(@1 / @2)", smapper.getF(out, 1), smapper.popF(out)); break; case ByteCodeParser.opc_ddiv: - smapper.replace(out, VarType.DOUBLE, "(@1 / @2)", smapper.getD(1), smapper.popD()); + smapper.replace(out, VarType.DOUBLE, "(@1 / @2)", smapper.getD(out, 1), smapper.popD(out)); break; case ByteCodeParser.opc_irem: - smapper.replace(out, VarType.INTEGER, numbers.mod32(), smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, numbers.mod32(), smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lrem: - smapper.replace(out, VarType.LONG, numbers.mod64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.mod64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_frem: - smapper.replace(out, VarType.FLOAT, "(@1 % @2)", smapper.getF(1), smapper.popF()); + smapper.replace(out, VarType.FLOAT, "(@1 % @2)", smapper.getF(out, 1), smapper.popF(out)); break; case ByteCodeParser.opc_drem: - smapper.replace(out, VarType.DOUBLE, "(@1 % @2)", smapper.getD(1), smapper.popD()); + smapper.replace(out, VarType.DOUBLE, "(@1 % @2)", smapper.getD(out, 1), smapper.popD(out)); break; case ByteCodeParser.opc_iand: - smapper.replace(out, VarType.INTEGER, "(@1 & @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 & @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_land: - smapper.replace(out, VarType.LONG, numbers.and64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.and64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_ior: - smapper.replace(out, VarType.INTEGER, "(@1 | @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 | @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lor: - smapper.replace(out, VarType.LONG, numbers.or64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.or64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_ixor: - smapper.replace(out, VarType.INTEGER, "(@1 ^ @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 ^ @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lxor: - smapper.replace(out, VarType.LONG, numbers.xor64(), smapper.getL(1), smapper.popL()); + smapper.replace(out, VarType.LONG, numbers.xor64(), smapper.getL(out, 1), smapper.popL(out)); break; case ByteCodeParser.opc_ineg: - smapper.replace(out, VarType.INTEGER, "(-(@1) | 0)", smapper.getI(0)); + smapper.replace(out, VarType.INTEGER, "(-(@1) | 0)", smapper.getI(out, 0)); break; case ByteCodeParser.opc_lneg: - smapper.replace(out, VarType.LONG, numbers.neg64(), smapper.getL(0)); + smapper.replace(out, VarType.LONG, numbers.neg64(), smapper.getL(out, 0)); break; case ByteCodeParser.opc_fneg: - smapper.replace(out, VarType.FLOAT, "(-@1)", smapper.getF(0)); + smapper.replace(out, VarType.FLOAT, "(-@1)", smapper.getF(out, 0)); break; case ByteCodeParser.opc_dneg: - smapper.replace(out, VarType.DOUBLE, "(-@1)", smapper.getD(0)); + smapper.replace(out, VarType.DOUBLE, "(-@1)", smapper.getD(out, 0)); break; case ByteCodeParser.opc_ishl: - smapper.replace(out, VarType.INTEGER, "(@1 << @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 << @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lshl: - smapper.replace(out, VarType.LONG, numbers.shl64(), smapper.getL(1), smapper.popI()); + smapper.replace(out, VarType.LONG, numbers.shl64(), smapper.getL(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_ishr: - smapper.replace(out, VarType.INTEGER, "(@1 >> @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 >> @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lshr: - smapper.replace(out, VarType.LONG, numbers.shr64(), smapper.getL(1), smapper.popI()); + smapper.replace(out, VarType.LONG, numbers.shr64(), smapper.getL(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_iushr: - smapper.replace(out, VarType.INTEGER, "(@1 >>> @2)", smapper.getI(1), smapper.popI()); + smapper.replace(out, VarType.INTEGER, "(@1 >>> @2)", smapper.getI(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_lushr: - smapper.replace(out, VarType.LONG, numbers.ushr64(), smapper.getL(1), smapper.popI()); + smapper.replace(out, VarType.LONG, numbers.ushr64(), smapper.getL(out, 1), smapper.popI(out)); break; case ByteCodeParser.opc_iinc: { @@ -425,63 +430,63 @@ void loopCode( ByteCodeToJavaScript.emit(out, smapper, "return;"); break; case ByteCodeParser.opc_ireturn: - ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popI()); + ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popI(out)); break; case ByteCodeParser.opc_lreturn: - ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popL()); + ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popL(out)); break; case ByteCodeParser.opc_freturn: - ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popF()); + ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popF(out)); break; case ByteCodeParser.opc_dreturn: - ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popD()); + ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popD(out)); break; case ByteCodeParser.opc_areturn: - ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "return @1;", smapper.popA(out)); break; case ByteCodeParser.opc_i2l: - smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getI(0)); + smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getI(out, 0)); break; case ByteCodeParser.opc_i2f: - smapper.replace(out, VarType.FLOAT, "@1", smapper.getI(0)); + smapper.replace(out, VarType.FLOAT, "@1", smapper.getI(out, 0)); break; case ByteCodeParser.opc_i2d: - smapper.replace(out, VarType.DOUBLE, "@1", smapper.getI(0)); + smapper.replace(out, VarType.DOUBLE, "@1", smapper.getI(out, 0)); break; case ByteCodeParser.opc_l2i: - smapper.replace(out, VarType.INTEGER, "((@1) | 0)", smapper.getL(0)); + smapper.replace(out, VarType.INTEGER, "((@1) | 0)", smapper.getL(out, 0)); break; // max int check? case ByteCodeParser.opc_l2f: - smapper.replace(out, VarType.FLOAT, numbers.toFP(), smapper.getL(0)); + smapper.replace(out, VarType.FLOAT, numbers.toFP(), smapper.getL(out, 0)); break; case ByteCodeParser.opc_l2d: - smapper.replace(out, VarType.DOUBLE, numbers.toFP(), smapper.getL(0)); + smapper.replace(out, VarType.DOUBLE, numbers.toFP(), smapper.getL(out, 0)); break; case ByteCodeParser.opc_f2d: - smapper.replace(out, VarType.DOUBLE, "@1", smapper.getF(0)); + smapper.replace(out, VarType.DOUBLE, "@1", smapper.getF(out, 0)); break; case ByteCodeParser.opc_d2f: - smapper.replace(out, VarType.FLOAT, "@1", smapper.getD(0)); + smapper.replace(out, VarType.FLOAT, "@1", smapper.getD(out, 0)); break; case ByteCodeParser.opc_f2i: - smapper.replace(out, VarType.INTEGER, "((@1) | 0)", smapper.getF(0)); + smapper.replace(out, VarType.INTEGER, "((@1) | 0)", smapper.getF(out, 0)); break; case ByteCodeParser.opc_f2l: - smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getF(0)); + smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getF(out, 0)); break; case ByteCodeParser.opc_d2i: - smapper.replace(out, VarType.INTEGER, "((@1)| 0)", smapper.getD(0)); + smapper.replace(out, VarType.INTEGER, "((@1)| 0)", smapper.getD(out, 0)); break; case ByteCodeParser.opc_d2l: - smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getD(0)); + smapper.replace(out, VarType.LONG, "(@1).toLong()", smapper.getD(out, 0)); break; case ByteCodeParser.opc_i2b: - smapper.replace(out, VarType.INTEGER, "(((@1) << 24) >> 24)", smapper.getI(0)); + smapper.replace(out, VarType.INTEGER, "(((@1) << 24) >> 24)", smapper.getI(out, 0)); break; case ByteCodeParser.opc_i2c: case ByteCodeParser.opc_i2s: - smapper.replace(out, VarType.INTEGER, "(((@1) << 16) >> 16)", smapper.getI(0)); + smapper.replace(out, VarType.INTEGER, "(((@1) << 16) >> 16)", smapper.getI(out, 0)); break; case ByteCodeParser.opc_aconst_null: smapper.assign(out, VarType.REFERENCE, "null"); @@ -558,101 +563,101 @@ void loopCode( break; } case ByteCodeParser.opc_lcmp: - smapper.replace(out, VarType.INTEGER, numbers.compare64(), smapper.popL(), smapper.getL(0)); + smapper.replace(out, VarType.INTEGER, numbers.compare64(), smapper.popL(out), smapper.getL(out, 0)); break; case ByteCodeParser.opc_fcmpl: case ByteCodeParser.opc_fcmpg: - ByteCodeToJavaScript.emit(out, smapper, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", smapper.popF(), smapper.popF(), smapper.pushI()); + ByteCodeToJavaScript.emit(out, smapper, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", smapper.popF(out), smapper.popF(out), smapper.pushI()); break; case ByteCodeParser.opc_dcmpl: case ByteCodeParser.opc_dcmpg: - ByteCodeToJavaScript.emit(out, smapper, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", smapper.popD(), smapper.popD(), smapper.pushI()); + ByteCodeToJavaScript.emit(out, smapper, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", smapper.popD(out), smapper.popD(out), smapper.pushI()); break; case ByteCodeParser.opc_if_acmpeq: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popA(), smapper.popA(), "===", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popA(out), smapper.popA(out), "===", topMostLabel); break; case ByteCodeParser.opc_if_acmpne: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popA(), smapper.popA(), "!==", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popA(out), smapper.popA(out), "!==", topMostLabel); break; case ByteCodeParser.opc_if_icmpeq: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), "==", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), "==", topMostLabel); break; case ByteCodeParser.opc_ifeq: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) == 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) == 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifne: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) != 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) != 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_iflt: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) < 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) < 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifle: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) <= 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) <= 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifgt: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) > 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) > 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifge: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) >= 0) ", smapper.popI(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) >= 0) ", smapper.popI(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifnonnull: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) !== null) ", smapper.popA(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) !== null) ", smapper.popA(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_ifnull: { int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) === null) ", smapper.popA(), i, indx, topMostLabel); + ByteCodeToJavaScript.emitIf(out, smapper, "if ((@1) === null) ", smapper.popA(out), i, indx, topMostLabel); i += 2; break; } case ByteCodeParser.opc_if_icmpne: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), "!=", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), "!=", topMostLabel); break; case ByteCodeParser.opc_if_icmplt: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), "<", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), "<", topMostLabel); break; case ByteCodeParser.opc_if_icmple: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), "<=", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), "<=", topMostLabel); break; case ByteCodeParser.opc_if_icmpgt: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), ">", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), ">", topMostLabel); break; case ByteCodeParser.opc_if_icmpge: - i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(), smapper.popI(), ">=", topMostLabel); + i = byteCodeToJavaScript.generateIf(out, smapper, byteCodes, i, smapper.popI(out), smapper.popI(out), ">=", topMostLabel); break; case ByteCodeParser.opc_goto: { smapper.flush(out); int indx = i + ByteCodeToJavaScript.readShortArg(byteCodes, i); - ByteCodeToJavaScript.goTo(out, i, indx, topMostLabel); + ByteCodeToJavaScript.goTo(out, i, indx, topMostLabel, smapper.alwaysUseGt()); i += 2; break; } @@ -720,52 +725,52 @@ void loopCode( break; } case ByteCodeParser.opc_arraylength: - smapper.replace(out, VarType.INTEGER, "(@1).length", smapper.getA(0)); + smapper.replace(out, VarType.INTEGER, "(@1).length", smapper.getA(out, 0)); break; case ByteCodeParser.opc_lastore: - ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popL(), smapper.popI(), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popL(out), smapper.popI(out), smapper.popA(out)); break; case ByteCodeParser.opc_fastore: - ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popF(), smapper.popI(), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popF(out), smapper.popI(out), smapper.popA(out)); break; case ByteCodeParser.opc_dastore: - ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popD(), smapper.popI(), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popD(out), smapper.popI(out), smapper.popA(out)); break; case ByteCodeParser.opc_aastore: - ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popA(), smapper.popI(), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popA(out), smapper.popI(out), smapper.popA(out)); break; case ByteCodeParser.opc_iastore: case ByteCodeParser.opc_bastore: case ByteCodeParser.opc_castore: case ByteCodeParser.opc_sastore: - ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popI(), smapper.popI(), smapper.popA()); + ByteCodeToJavaScript.emit(out, smapper, "Array.at(@3, @2, @1);", smapper.popI(out), smapper.popI(out), smapper.popA(out)); break; case ByteCodeParser.opc_laload: - smapper.replace(out, VarType.LONG, "(@2[@1] || Array.at(@2, @1))", smapper.popI(), smapper.getA(0)); + smapper.replace(out, VarType.LONG, "(@2[@1] || Array.at(@2, @1))", smapper.popI(out), smapper.getA(out, 0)); break; case ByteCodeParser.opc_faload: - smapper.replace(out, VarType.FLOAT, "(@2[@1] || Array.at(@2, @1))", smapper.popI(), smapper.getA(0)); + smapper.replace(out, VarType.FLOAT, "(@2[@1] || Array.at(@2, @1))", smapper.popI(out), smapper.getA(out, 0)); break; case ByteCodeParser.opc_daload: - smapper.replace(out, VarType.DOUBLE, "(@2[@1] || Array.at(@2, @1))", smapper.popI(), smapper.getA(0)); + smapper.replace(out, VarType.DOUBLE, "(@2[@1] || Array.at(@2, @1))", smapper.popI(out), smapper.getA(out, 0)); break; case ByteCodeParser.opc_aaload: - smapper.replace(out, VarType.REFERENCE, "(@2[@1] || Array.at(@2, @1))", smapper.popI(), smapper.getA(0)); + smapper.replace(out, VarType.REFERENCE, "(@2[@1] || Array.at(@2, @1))", smapper.popI(out), smapper.getA(out, 0)); break; case ByteCodeParser.opc_iaload: case ByteCodeParser.opc_baload: case ByteCodeParser.opc_caload: case ByteCodeParser.opc_saload: - smapper.replace(out, VarType.INTEGER, "(@2[@1] || Array.at(@2, @1))", smapper.popI(), smapper.getA(0)); + smapper.replace(out, VarType.INTEGER, "(@2[@1] || Array.at(@2, @1))", smapper.popI(out), smapper.getA(out, 0)); break; case ByteCodeParser.opc_pop: case ByteCodeParser.opc_pop2: - smapper.pop(1); + smapper.pop(out, 1); byteCodeToJavaScript.debug(out, "/* pop */"); break; case ByteCodeParser.opc_dup: { - final Variable v = smapper.get(0); + final Variable v = smapper.get(out, 0); if (smapper.isDirty()) { ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", smapper.pushT(v.getType()), v); } else { @@ -775,11 +780,11 @@ void loopCode( } case ByteCodeParser.opc_dup2: { - final Variable vi1 = smapper.get(0); + final Variable vi1 = smapper.get(out, 0); if (vi1.isCategory2()) { ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2;", smapper.pushT(vi1.getType()), vi1); } else { - final Variable vi2 = smapper.get(1); + final Variable vi2 = smapper.get(out, 1); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4;", smapper.pushT(vi2.getType()), vi2, smapper.pushT(vi1.getType()), vi1); } break; @@ -788,9 +793,9 @@ void loopCode( { final Variable vi1 = smapper.pop(out); final Variable vi2 = smapper.pop(out); - final Variable vo3 = smapper.pushT(vi1.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi1.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6;", vo1, vi1, vo2, vi2, vo3, vo1); break; } @@ -799,17 +804,17 @@ void loopCode( final Variable vi1 = smapper.pop(out); final Variable vi2 = smapper.pop(out); if (vi1.isCategory2()) { - final Variable vo3 = smapper.pushT(vi1.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi1.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6;", vo1, vi1, vo2, vi2, vo3, vo1); } else { final Variable vi3 = smapper.pop(out); - final Variable vo5 = smapper.pushT(vi2.getType()); - final Variable vo4 = smapper.pushT(vi1.getType()); - final Variable vo3 = smapper.pushT(vi3.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo5 = smapper.pushT(vi2.getType()); + final CharSequence vo4 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi3.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6,", vo1, vi1, vo2, vi2, vo3, vi3); ByteCodeToJavaScript.emit(out, smapper, " @1 = @2, @3 = @4;", vo4, vo1, vo5, vo2); } @@ -820,16 +825,16 @@ void loopCode( final Variable vi1 = smapper.pop(out); final Variable vi2 = smapper.pop(out); if (vi2.isCategory2()) { - final Variable vo3 = smapper.pushT(vi1.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi1.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6;", vo1, vi1, vo2, vi2, vo3, vo1); } else { final Variable vi3 = smapper.pop(out); - final Variable vo4 = smapper.pushT(vi1.getType()); - final Variable vo3 = smapper.pushT(vi3.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo4 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi3.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8;", vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1); } break; @@ -840,36 +845,36 @@ void loopCode( final Variable vi2 = smapper.pop(out); if (vi1.isCategory2()) { if (vi2.isCategory2()) { - final Variable vo3 = smapper.pushT(vi1.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi1.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6;", vo1, vi1, vo2, vi2, vo3, vo1); } else { final Variable vi3 = smapper.pop(out); - final Variable vo4 = smapper.pushT(vi1.getType()); - final Variable vo3 = smapper.pushT(vi3.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo4 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi3.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8;", vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1); } } else { final Variable vi3 = smapper.pop(out); if (vi3.isCategory2()) { - final Variable vo5 = smapper.pushT(vi2.getType()); - final Variable vo4 = smapper.pushT(vi1.getType()); - final Variable vo3 = smapper.pushT(vi3.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo5 = smapper.pushT(vi2.getType()); + final CharSequence vo4 = smapper.pushT(vi1.getType()); + final CharSequence vo3 = smapper.pushT(vi3.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6,", vo1, vi1, vo2, vi2, vo3, vi3); ByteCodeToJavaScript.emit(out, smapper, " @1 = @2, @3 = @4;", vo4, vo1, vo5, vo2); } else { final Variable vi4 = smapper.pop(out); - final Variable vo6 = smapper.pushT(vi2.getType()); - final Variable vo5 = smapper.pushT(vi1.getType()); - final Variable vo4 = smapper.pushT(vi4.getType()); - final Variable vo3 = smapper.pushT(vi3.getType()); - final Variable vo2 = smapper.pushT(vi2.getType()); - final Variable vo1 = smapper.pushT(vi1.getType()); + final CharSequence vo6 = smapper.pushT(vi2.getType()); + final CharSequence vo5 = smapper.pushT(vi1.getType()); + final CharSequence vo4 = smapper.pushT(vi4.getType()); + final CharSequence vo3 = smapper.pushT(vi3.getType()); + final CharSequence vo2 = smapper.pushT(vi2.getType()); + final CharSequence vo1 = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8,", vo1, vi1, vo2, vi2, vo3, vi3, vo4, vi4); ByteCodeToJavaScript.emit(out, smapper, " @1 = @2, @3 = @4;", vo5, vo1, vo6, vo2); } @@ -878,11 +883,11 @@ void loopCode( } case ByteCodeParser.opc_swap: { - final Variable vi1 = smapper.get(0); - final Variable vi2 = smapper.get(1); - final Variable tmp = smapper.pushT(vi1.getType()); + final Variable vi1 = smapper.get(out, 0); + final Variable vi2 = smapper.get(out, 1); + final CharSequence tmp = smapper.pushT(vi1.getType()); ByteCodeToJavaScript.emit(out, smapper, "var @1 = @2, @2 = @3, @3 = @1;", tmp, vi1, vi2); - smapper.pop(1); + smapper.pop(out, 1); break; } case ByteCodeParser.opc_bipush: @@ -901,10 +906,10 @@ void loopCode( if (field == null) { final String mangleClass = ByteCodeToJavaScript.mangleClassName(fi[0]); final String mangleClassAccess = byteCodeToJavaScript.accessClassFalse(mangleClass); - smapper.replace(out, type, "@2.call(@1)", smapper.getA(0), byteCodeToJavaScript.accessField(mangleClassAccess, null, fi)); + smapper.replace(out, type, "@2.call(@1)", smapper.getA(out, 0), byteCodeToJavaScript.accessField(mangleClassAccess, null, fi)); } else { final String fieldOwner = ByteCodeToJavaScript.mangleClassName(field.cls.getClassName()); - smapper.replace(out, type, "@1@2", smapper.getA(0), byteCodeToJavaScript.accessField(fieldOwner, field, fi)); + smapper.replace(out, type, "@1@2", smapper.getA(out, 0), byteCodeToJavaScript.accessField(fieldOwner, field, fi)); } i += 2; byteCodeToJavaScript.addReference(out, fi[0]); @@ -919,10 +924,10 @@ void loopCode( if (field == null) { final String mangleClass = ByteCodeToJavaScript.mangleClassName(fi[0]); final String mangleClassAccess = byteCodeToJavaScript.accessClassFalse(mangleClass); - ByteCodeToJavaScript.emit(out, smapper, "@3.call(@2, @1);", smapper.popT(type), smapper.popA(), byteCodeToJavaScript.accessField(mangleClassAccess, null, fi)); + ByteCodeToJavaScript.emit(out, smapper, "@3.call(@2, @1);", smapper.popT(out, type), smapper.popA(out), byteCodeToJavaScript.accessField(mangleClassAccess, null, fi)); } else { final String fieldOwner = ByteCodeToJavaScript.mangleClassName(field.cls.getClassName()); - ByteCodeToJavaScript.emit(out, smapper, "@2@3 = @1;", smapper.popT(type), smapper.popA(), byteCodeToJavaScript.accessField(fieldOwner, field, fi)); + ByteCodeToJavaScript.emit(out, smapper, "@2@3 = @1;", smapper.popT(out, type), smapper.popA(out), byteCodeToJavaScript.accessField(fieldOwner, field, fi)); } i += 2; byteCodeToJavaScript.addReference(out, fi[0]); @@ -946,7 +951,7 @@ void loopCode( int indx = ByteCodeToJavaScript.readUShortArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - ByteCodeToJavaScript.emit(out, smapper, "@1._@2(@3);", byteCodeToJavaScript.accessClassFalse(ByteCodeToJavaScript.mangleClassName(fi[0])), fi[1], smapper.popT(type)); + ByteCodeToJavaScript.emit(out, smapper, "@1._@2(@3);", byteCodeToJavaScript.accessClassFalse(ByteCodeToJavaScript.mangleClassName(fi[0])), fi[1], smapper.popT(out, type)); i += 2; byteCodeToJavaScript.addReference(out, fi[0]); break; @@ -967,21 +972,21 @@ void loopCode( } case ByteCodeParser.opc_athrow: { - final CharSequence v = smapper.popA(); + final CharSequence v = smapper.popA(out); smapper.clear(); ByteCodeToJavaScript.emit(out, smapper, "{ var @1 = @2; throw @2; }", smapper.pushA(), v); break; } case ByteCodeParser.opc_monitorenter: { - byteCodeToJavaScript.debug(null, "/* monitor enter */"); - smapper.popA(); + byteCodeToJavaScript.debug(out, "/* monitor enter */"); + smapper.popA(out); break; } case ByteCodeParser.opc_monitorexit: { - byteCodeToJavaScript.debug(null, "/* monitor exit */"); - smapper.popA(); + byteCodeToJavaScript.debug(out, "/* monitor exit */"); + smapper.popA(out); break; } case ByteCodeParser.opc_wide: @@ -999,9 +1004,10 @@ void loopCode( if (modified) { out.append("\n"); } + smapper.finishStatement(out); } if (previousTrap != null) { - byteCodeToJavaScript.generateCatch(out, previousTrap, byteCodes.length, topMostLabel); + byteCodeToJavaScript.generateCatch(out, smapper, previousTrap, byteCodes.length, topMostLabel, smapper.alwaysUseGt()); } if (didBranches) { out.append("\n }\n"); @@ -1011,7 +1017,7 @@ void loopCode( } } - private void handleIndy(int indx, String[] methodAndType, String[] mi, ByteCodeParser.BootMethodData bm, StackMapper mapper) throws IOException { + private void handleIndy(int indx, String[] methodAndType, String[] mi, ByteCodeParser.BootMethodData bm, AbstractStackMapper mapper) throws IOException { for (IndyHandler h : byteCodeToJavaScript.getIndyHandlers()) { if (h.factoryClazz.equals(mi[0]) && h.factoryMethod.equals(mi[1])) { IndyHandler.Ctx ctx = new IndyHandler.Ctx(out, mapper, byteCodeToJavaScript, methodAndType, bm); @@ -1033,7 +1039,7 @@ private void handleIndy(int indx, String[] methodAndType, String[] mi, ByteCodeP mapper.pushT(type); } - int invokeVirtualMethod(Appendable out, byte[] byteCodes, int i, final StackMapper mapper) throws IOException { + int invokeVirtualMethod(Appendable out, byte[] byteCodes, int i, final AbstractStackMapper mapper) throws IOException { int methodIndex = ByteCodeToJavaScript.readUShortArg(byteCodes, i); String[] mi = jc.getFieldInfoName(methodIndex); char[] returnType = {'V'}; @@ -1042,7 +1048,7 @@ int invokeVirtualMethod(Appendable out, byte[] byteCodes, int i, final StackMapp final int numArguments = cnt.length() + 1; final CharSequence[] vars = new CharSequence[numArguments]; for (int j = numArguments - 1; j >= 0; --j) { - vars[j] = mapper.popValue(); + vars[j] = mapper.popValue(out); } if (returnType[0] != 'V') { mapper.flush(out); @@ -1063,7 +1069,7 @@ int invokeVirtualMethod(Appendable out, byte[] byteCodes, int i, final StackMapp return i; } - int invokeStaticMethod(Appendable out, byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic) throws IOException { + int invokeStaticMethod(Appendable out, byte[] byteCodes, int i, final AbstractStackMapper mapper, boolean isStatic) throws IOException { int methodIndex = ByteCodeToJavaScript.readUShortArg(byteCodes, i); String[] mi = jc.getFieldInfoName(methodIndex); char[] returnType = {'V'}; @@ -1072,7 +1078,7 @@ int invokeStaticMethod(Appendable out, byte[] byteCodes, int i, final StackMappe final int numArguments = isStatic ? cnt.length() : cnt.length() + 1; final CharSequence[] vars = new CharSequence[numArguments]; for (int j = numArguments - 1; j >= 0; --j) { - vars[j] = mapper.popValue(); + vars[j] = mapper.popValue(out); } if ("java/lang/Object".equals(mi[0]) && "".equals(mi[1])) { return i + 2; diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/MetafactoryHandler.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/MetafactoryHandler.java index 64440b0fd..8e10ee382 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/MetafactoryHandler.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/MetafactoryHandler.java @@ -78,14 +78,14 @@ protected boolean handle(Ctx ctx) throws IOException { fixedArgsCount = cnt.length(); final CharSequence[] vars = new CharSequence[fixedArgsCount]; for (int j = fixedArgsCount - 1; j >= 0; --j) { - vars[j] = ctx.stackMapper.popValue(); + vars[j] = ctx.stackMapper.popValue(ctx.out); } assert returnType[0] == 'L'; ctx.stackMapper.flush(ctx.out); - final Variable samVar = ctx.stackMapper.pushA(); + final CharSequence samVar = ctx.stackMapper.pushA(); ctx.out.append("var ").append(samVar).append(" = ").append(interfaceToCreate).append(".constructor.$class.$lambda(["); String sep = ""; diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/OldStackMapper.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/OldStackMapper.java new file mode 100644 index 000000000..1ed514849 --- /dev/null +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/OldStackMapper.java @@ -0,0 +1,132 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; + +final class OldStackMapper extends AbstractStackMapper { + int counter; + int flushed; + + OldStackMapper() { + } + + @Override + public void clear() { + } + + @Override + public void syncWithFrameStack(ByteCodeParser.TypeArray frameStack) { + } + + @Override + int initCode(Appendable out) throws IOException { + out.append(" var gt = 0;\n"); + out.append(" var stack = [];\n"); + out.append(" X_0: for (;;) {\n"); + return 1; + } + + @Override + public CharSequence pushT(int type) { + return Variable.getStackVariable(VarType.REFERENCE, ++counter); + } + + @Override + void assign(Appendable out, int varType, CharSequence s) throws IOException { + out.append("stack.push(").append(s).append(");"); + } + + @Override + void replace(Appendable out, int varType, String format, CharSequence... arr) throws IOException { + out.append("stack[stack.length - 1] = "); + ByteCodeToJavaScript.emitImpl(out, format, arr); + out.append(";\n"); + } + + @Override + void flush(Appendable out) throws IOException { + } + + @Override + void finishStatement(Appendable out) throws IOException { + while (flushed < counter) { + out.append("stack.push(").append(Variable.getStackVariable(VarType.REFERENCE, ++flushed)).append(");\n"); + } + out.append("}\n"); + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public CharSequence popT(Appendable out, int type) throws IOException { + Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter); + out.append("var ").append(v).append(" = stack.pop();\n"); + flushed = counter; + return v; + } + + @Override + public CharSequence popValue(Appendable out) throws IOException { + return pop(out); + } + + @Override + public Variable pop(Appendable out) throws IOException { + Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter); + out.append("var ").append(v).append(" = stack.pop();\n"); + flushed = counter; + return v; + } + + @Override + public void pop(Appendable out, int count) throws IOException { + while (count-- > 0) { + out.append("stack.pop();"); + } + } + + @Override + public CharSequence getT(Appendable out, int indexFromTop, int type, boolean clear) throws IOException { + Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter); + out.append("var ").append(v).append(" = stack[stack.length - " + (indexFromTop + 1) + "];\n"); + flushed = counter; + return v; + } + + @Override + public Variable get(Appendable out, int indexFromTop) throws IOException { + Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter); + out.append("var ").append(v).append(" = stack[stack.length - " + (indexFromTop + 1) + "];\n"); + flushed = counter; + return v; + } + + @Override + boolean alwaysUseGt() { + return true; + } + + @Override + void caughtException(Appendable out, String e) throws IOException { + out.append("stack = [").append(e).append("];"); + } +} diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java index 0fc02d316..788d7a6c3 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java @@ -20,7 +20,7 @@ import java.io.IOException; import org.apidesign.vm4brwsr.ByteCodeParser.TypeArray; -final class StackMapper { +final class StackMapper extends AbstractStackMapper { private final TypeArray stackTypeIndexPairs; private final StringArray stackValues; private boolean dirty; @@ -45,34 +45,17 @@ public void syncWithFrameStack(final TypeArray frameStack) { } } - public Variable pushI() { - return pushT(VarType.INTEGER); - } - - public Variable pushL() { - return pushT(VarType.LONG); - } - - public Variable pushF() { - return pushT(VarType.FLOAT); - } - - public Variable pushD() { - return pushT(VarType.DOUBLE); - } - - public Variable pushA() { - return pushT(VarType.REFERENCE); - } - + @Override public Variable pushT(final int type) { return getVariable(pushTypeImpl(type)); } + @Override void assign(Appendable out, int varType, CharSequence s) throws IOException { pushTypeAndValue(varType, s); } + @Override void replace(Appendable out, int varType, String format, CharSequence... arr) throws IOException { StringBuilder sb = new StringBuilder(); @@ -84,7 +67,12 @@ void replace(Appendable out, int varType, String format, CharSequence... arr) final int value = (last << 8) | (varType & 0xff); stackTypeIndexPairs.set(last, value); } + + @Override + void finishStatement(Appendable out) throws IOException { + } + @Override void flush(Appendable out) throws IOException { int count = stackTypeIndexPairs.getSize(); for (int i = 0; i < count; i++) { @@ -98,49 +86,34 @@ void flush(Appendable out) throws IOException { dirty = false; } + @Override public boolean isDirty() { return dirty; } - public CharSequence popI() { - return popT(VarType.INTEGER); - } - - public CharSequence popL() { - return popT(VarType.LONG); - } - - public CharSequence popF() { - return popT(VarType.FLOAT); - } - - public CharSequence popD() { - return popT(VarType.DOUBLE); - } - - public CharSequence popA() { - return popT(VarType.REFERENCE); - } - - public CharSequence popT(final int type) { - final CharSequence variable = getT(0, type); + @Override + public CharSequence popT(Appendable out, final int type) throws IOException { + final CharSequence variable = getT(out, 0, type); popImpl(1); return variable; } - public CharSequence popValue() { - final CharSequence variable = getT(0, -1); + @Override + public CharSequence popValue(Appendable out) throws IOException { + final CharSequence variable = getT(out, 0, -1); popImpl(1); return variable; } + @Override public Variable pop(Appendable out) throws IOException { flush(out); - final Variable variable = get(0); + final Variable variable = get(out, 0); popImpl(1); return variable; } - public void pop(final int count) { + @Override + public void pop(Appendable out, final int count) { final int stackSize = stackTypeIndexPairs.getSize(); if (count > stackSize) { throw new IllegalStateException("Stack underflow"); @@ -148,30 +121,8 @@ public void pop(final int count) { popImpl(count); } - public CharSequence getI(final int indexFromTop) { - return getT(indexFromTop, VarType.INTEGER); - } - - public CharSequence getL(final int indexFromTop) { - return getT(indexFromTop, VarType.LONG); - } - - public CharSequence getF(final int indexFromTop) { - return getT(indexFromTop, VarType.FLOAT); - } - - public CharSequence getD(final int indexFromTop) { - return getT(indexFromTop, VarType.DOUBLE); - } - - public CharSequence getA(final int indexFromTop) { - return getT(indexFromTop, VarType.REFERENCE); - } - - public CharSequence getT(final int indexFromTop, final int type) { - return getT(indexFromTop, type, true); - } - public CharSequence getT(final int indexFromTop, final int type, boolean clear) { + @Override + public CharSequence getT(Appendable out, final int indexFromTop, final int type, boolean clear) { final int stackSize = stackTypeIndexPairs.getSize(); if (indexFromTop >= stackSize) { throw new IllegalStateException("Stack underflow"); @@ -189,7 +140,8 @@ public CharSequence getT(final int indexFromTop, final int type, boolean clear) return getVariable(stackValue); } - public Variable get(final int indexFromTop) { + @Override + public Variable get(Appendable out, final int indexFromTop) { final int stackSize = stackTypeIndexPairs.getSize(); if (indexFromTop >= stackSize) { throw new IllegalStateException("Stack underflow"); @@ -232,10 +184,18 @@ private void popImpl(final int count) { stackTypeIndexPairs.setSize(stackSize - count); } - public Variable getVariable(final int typeAndIndex) { - final int type = typeAndIndex & 0xff; - final int index = typeAndIndex >> 8; + @Override + boolean alwaysUseGt() { + return false; + } + + @Override + int initCode(Appendable out) throws IOException { + return 0; + } - return Variable.getStackVariable(type, index); + @Override + void caughtException(Appendable out, String e) throws IOException { + out.append("var stA0 = ").append(e).append(";"); } } diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringConcatHandler.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringConcatHandler.java index 8338748a3..f023eef71 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringConcatHandler.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringConcatHandler.java @@ -38,13 +38,13 @@ protected boolean handle(Ctx ctx) throws IOException { fixedArgsCount = cnt.length(); final CharSequence[] vars = new CharSequence[fixedArgsCount]; for (int j = fixedArgsCount - 1; j >= 0; --j) { - vars[j] = ctx.stackMapper.popValue(); + vars[j] = ctx.stackMapper.popValue(ctx.out); } ctx.stackMapper.flush(ctx.out); - final Variable stringVar = ctx.stackMapper.pushA(); + final CharSequence stringVar = ctx.stackMapper.pushA(); ctx.out.append("\nvar ").append(stringVar).append(" = "); StringBuilder sep = new StringBuilder(); diff --git a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java index 371c34782..b371cc480 100644 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java @@ -224,7 +224,12 @@ private void generateBody(Appendable out, StringArray names) throws IOException if (name == null) { break; } - InputStream is = resources.get(name + ".class"); + InputStream is; + try { + is = resources.get(name + ".class"); + } catch (IOException noSuchResource) { + is = null; + } if (is == null) { lazyReference(out, name); skipClass.add(name); @@ -235,7 +240,7 @@ private void generateBody(Appendable out, StringArray names) throws IOException processed.add(name); initCode.add(ic == null ? "" : ic); } catch (RuntimeException ex) { - throw new IOException("Error while compiling " + name + "\n", ex); + throw new IOException("Error while compiling " + name + "\n" + ex.getClass().getName() + ": " + ex.getMessage(), ex); } } diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java index b04becd56..43ee54d0a 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java @@ -46,7 +46,8 @@ static byte[] readClass(String name) throws IOException { if (u == null) { throw new IOException("Can't find " + name); } - try (InputStream is = u.openStream()) { + InputStream is = u.openStream(); + try { byte[] arr; arr = new byte[is.available()]; int offset = 0; @@ -58,6 +59,8 @@ static byte[] readClass(String name) throws IOException { offset += len; } return arr; + } finally { + is.close(); } } diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java index 176ad0178..420daf1d9 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java @@ -281,7 +281,7 @@ public static Object reflectiveMethodCall(boolean direct, String mn) throws Exce public static int reflectiveSum(int a, int b) throws Exception { Method m = StaticMethod.class.getMethod("sum", int.class, int.class); - return (int) m.invoke(null, a, b); + return ((Number) m.invoke(null, a, b)).intValue(); } private abstract class Application { diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java index 0408f8844..462685bfd 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java @@ -71,7 +71,9 @@ public static String newInstance(String n) { return ("CNFE:" + ex.getMessage()).toString(); } return c.newInstance().getClass().getName(); - } catch (InstantiationException | IllegalAccessException ex) { + } catch (InstantiationException ex) { + return ex.getMessage(); + } catch (IllegalAccessException ex) { return ex.getMessage(); } } diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/SizeOfAMethodTest.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/SizeOfAMethodTest.java index e9ab684fd..f98306477 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/SizeOfAMethodTest.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/SizeOfAMethodTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStream; import static org.testng.Assert.*; +import org.testng.SkipException; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -37,7 +38,8 @@ public class SizeOfAMethodTest { private static String code; - @Test public void sumXYShouldBeSmall() { + @Test + public void sumXYShouldBeSmall() { String s = code; int beg = s.indexOf("c.sum__III"); int end = s.indexOf(".access", beg); @@ -46,7 +48,8 @@ public class SizeOfAMethodTest { assertTrue(beg < end, "Found end of sum method in " + code); String method = s.substring(beg, end); - + + assumeNewStackMapper(method); assertEquals(method.indexOf("st"), -1, "There should be no stack operations:\n" + method); } @@ -61,10 +64,13 @@ public class SizeOfAMethodTest { String method = s.substring(beg, end); + assumeNewStackMapper(method); + assertEquals(method.indexOf("stA1"), -1, "No need for stA1 register:\n" + method); } - @Test public void deepConstructor() { + @Test + public void deepConstructor() { String s = code; int beg = s.indexOf("c.intHolder__I"); int end = s.indexOf(".access", beg); @@ -73,11 +79,14 @@ public class SizeOfAMethodTest { assertTrue(beg < end, "Found end of intHolder method in " + code); String method = s.substring(beg, end); + + assumeNewStackMapper(method); assertEquals(method.indexOf("stA3"), -1, "No need for stA3 register on second constructor:\n" + method); } - @Test public void emptyConstructorRequiresNoStack() { + @Test + public void emptyConstructorRequiresNoStack() { String s = code; int beg = s.indexOf("CLS.cons__V"); int end = s.indexOf(".access", beg); @@ -87,12 +96,15 @@ public class SizeOfAMethodTest { String method = s.substring(beg, end); method = method.replace("constructor", "CNSTR"); + + assumeNewStackMapper(method); assertEquals(method.indexOf("st"), -1, "There should be no stack operations:\n" + method); assertEquals(method.indexOf("for"), -1, "There should be no for blocks:\n" + method); } - @Test public void dontGeneratePrimitiveFinalConstants() { + @Test + public void dontGeneratePrimitiveFinalConstants() { assertEquals(code.indexOf("MISSING_CONSTANT"), -1, "MISSING_CONSTANT field should not be generated"); } @@ -117,5 +129,12 @@ public InputStream get(String resource) throws IOException { public static void releaseTheCode() { code = null; } + + private static void assumeNewStackMapper(String code) { + int stackArray = code.indexOf("var stack = [];"); + if (stackArray >= 0) { + throw new SkipException("Skipping the text as code contains stack = []"); + } + } } diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StackMapperTest.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StackMapperTest.java index d96ae392b..2b33fa47f 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StackMapperTest.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StackMapperTest.java @@ -34,28 +34,28 @@ public StackMapperTest() { throws Exception { StringBuilder sb = new StringBuilder(); - StackMapper smapper = new StackMapper(); + AbstractStackMapper smapper = new StackMapper(); smapper.assign(sb, VarType.INTEGER, "0"); smapper.assign(sb, VarType.REFERENCE, "arr"); smapper.assign(sb, VarType.INTEGER, "33"); smapper.replace(sb, VarType.INTEGER, "@2[@1]", - smapper.popI(), smapper.getA(0) + smapper.popI(sb), smapper.getA(sb, 0) ); smapper.replace(sb, VarType.INTEGER, "(@1) + (@2)", - smapper.getI(1), smapper.popI() + smapper.getI(sb, 1), smapper.popI(sb) ); smapper.assign(sb, VarType.REFERENCE, "arr"); smapper.assign(sb, VarType.INTEGER, "22"); smapper.replace(sb, VarType.INTEGER, "@2[@1]", - smapper.popI(), smapper.getA(0) + smapper.popI(sb), smapper.getA(sb, 0) ); smapper.replace(sb, VarType.INTEGER, "(@1) + (@2)", - smapper.getI(1), smapper.popI() + smapper.getI(sb, 1), smapper.popI(sb) ); smapper.flush(sb); diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java index fbbc96d52..4b66d3e32 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java @@ -104,12 +104,10 @@ public static int countAB(String txt) { } public static int stringSwitch(String txt) { - switch (txt) { - case "jedna": return 1; - case "dve": return 2; - case "tri": return 3; - case "ctyri": return 4; - } + if ("jedna".equals(txt)) return 1; + if ("dve".equals(txt)) return 2; + if ("tri".equals(txt)) return 3; + if ("ctyri".equals(txt)) return 4; return -1; } diff --git a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java index d3da77edc..f46e3fec9 100644 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java @@ -85,13 +85,24 @@ public class StringTest { ); } - @Test(timeOut=10000) public void toStringConcatenation() throws Exception { + @Test + public void toStringConcatenation1() throws Exception { + assertExec( + "Five executions should generate 5Hello World!", + StringSample.class, "toStringTest__Ljava_lang_String_2I", + "Hello World!1", 1 + ); + } + + @Test + public void toStringConcatenation5() throws Exception { assertExec( "Five executions should generate 5Hello World!", StringSample.class, "toStringTest__Ljava_lang_String_2I", "Hello World!5", 5 ); } + @Test public void toStringConcatenationJava() throws Exception { assertEquals("Hello World!5", StringSample.toStringTest(5)); }