From 4c0c7872a1d55b9c30da3181f46a58fc8b4b8ea4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 17 Mar 2021 21:11:18 +0300 Subject: [PATCH] JS: add support for CCE in strict mode --- .../ast/decompilation/StatementGenerator.java | 7 +- .../backend/javascript/JavaScriptTarget.java | 7 +- .../rendering/NameFrequencyEstimator.java | 36 +++- .../javascript/rendering/Renderer.java | 3 +- .../rendering/RenderingContext.java | 9 +- .../javascript/rendering/RuntimeRenderer.java | 17 ++ .../rendering/StatementRenderer.java | 78 ++++++--- .../AbstractInstructionAnalyzer.java | 8 + .../dependency/DependencyGraphBuilder.java | 2 + .../model/optimization/Devirtualization.java | 159 +++++++++++++----- .../org/teavm/backend/javascript/runtime.js | 12 ++ .../org/teavm/jso/impl/JSClassProcessor.java | 7 +- .../org/teavm/jso/impl/JSValueMarshaller.java | 28 +-- .../impl/CompositeMethodGenerator.java | 2 +- .../plugin/ResourceProgramTransformer.java | 18 +- 15 files changed, 276 insertions(+), 117 deletions(-) diff --git a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java index 6bd14b708..ec8c955a4 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java +++ b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java @@ -26,6 +26,7 @@ import org.teavm.ast.AssignmentStatement; import org.teavm.ast.BinaryOperation; import org.teavm.ast.BoundCheckExpr; import org.teavm.ast.BreakStatement; +import org.teavm.ast.CastExpr; import org.teavm.ast.ContinueStatement; import org.teavm.ast.Expr; import org.teavm.ast.InitClassStatement; @@ -208,7 +209,11 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(CastInstruction insn) { - assign(Expr.var(insn.getValue().getIndex()), insn.getReceiver()); + CastExpr expr = new CastExpr(); + expr.setLocation(insn.getLocation()); + expr.setValue(Expr.var(insn.getValue().getIndex())); + expr.setTarget(insn.getTargetType()); + assign(expr, insn.getReceiver()); } @Override diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 037415775..18ed985e0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -284,6 +284,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { NullPointerException.class, "", void.class)); exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NullPointerException.class.getName())); exceptionCons.use(); + + exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference( + ClassCastException.class, "", void.class)); + exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(ClassCastException.class.getName())); + exceptionCons.use(); } if (stackTraceIncluded) { @@ -370,7 +375,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { controller.getUnprocessedClassSource(), classes, controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming, controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m), - controller.getClassInitializerInfo()); + controller.getClassInitializerInfo(), strict); renderingContext.setMinifying(obfuscated); Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, controller.getDiagnostics(), renderingContext); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java index 74053018d..4b02a3ee0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java @@ -22,6 +22,7 @@ import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.BinaryExpr; import org.teavm.ast.BoundCheckExpr; +import org.teavm.ast.CastExpr; import org.teavm.ast.ConstantExpr; import org.teavm.ast.InitClassStatement; import org.teavm.ast.InstanceOfExpr; @@ -69,13 +70,16 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit private boolean async; private final Set injectedMethods; private final Set asyncFamilyMethods; + private final boolean strict; NameFrequencyEstimator(NameFrequencyConsumer consumer, ClassReaderSource classSource, - Set injectedMethods, Set asyncFamilyMethods) { + Set injectedMethods, Set asyncFamilyMethods, + boolean strict) { this.consumer = consumer; this.classSource = classSource; this.injectedMethods = injectedMethods; this.asyncFamilyMethods = asyncFamilyMethods; + this.strict = strict; } public void estimate(PreparedClass cls) { @@ -470,17 +474,33 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit public void visit(InstanceOfExpr expr) { super.visit(expr); visitType(expr.getType()); - if (expr.getType() instanceof ValueType.Object) { - String clsName = ((ValueType.Object) expr.getType()).getClassName(); - ClassReader cls = classSource.get(clsName); - if (cls == null || cls.hasModifier(ElementModifier.INTERFACE)) { - consumer.consumeFunction("$rt_isInstance"); - } - } else { + if (!isClass(expr.getType())) { consumer.consumeFunction("$rt_isInstance"); } } + @Override + public void visit(CastExpr expr) { + super.visit(expr); + if (strict) { + visitType(expr.getTarget()); + if (isClass(expr.getTarget())) { + consumer.consumeFunction("$rt_castToClass"); + } else { + consumer.consumeFunction("$rt_castToInterface"); + } + } + } + + private boolean isClass(ValueType type) { + if (!(type instanceof ValueType.Object)) { + return false; + } + String className = ((ValueType.Object) type).getClassName(); + ClassReader cls = classSource.get(className); + return cls != null && !cls.hasModifier(ElementModifier.INTERFACE); + } + @Override public void visit(BoundCheckExpr expr) { super.visit(expr); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index 149d1422b..df81bd32c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -261,6 +261,7 @@ public class Renderer implements RenderingManager { "$rt_createLongArrayFromData", "$rt_createBooleanArray", "$rt_createByteArray", "$rt_createShortArray", "$rt_createCharArray", "$rt_createIntArray", "$rt_createLongArray", "$rt_createFloatArray", "$rt_createDoubleArray", "$rt_compare", + "$rt_castToClass", "$rt_castToInterface", "Long_toNumber", "Long_fromInt", "Long_fromNumber", "Long_create", "Long_ZERO", "Long_hi", "Long_lo"); } @@ -287,7 +288,7 @@ public class Renderer implements RenderingManager { if (minifying) { NamingOrderer orderer = new NamingOrderer(); NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods, - asyncFamilyMethods); + asyncFamilyMethods, context.isStrict()); for (PreparedClass cls : classes) { estimator.estimate(cls); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index f1f7893e7..17b8dbc62 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -65,12 +65,14 @@ public class RenderingContext { private boolean minifying; private ClassInitializerInfo classInitializerInfo; private TextLocation lastEmittedLocation = TextLocation.EMPTY; + private boolean strict; public RenderingContext(DebugInformationEmitter debugEmitter, ClassReaderSource initialClassSource, ListableClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Properties properties, NamingStrategy naming, DependencyInfo dependencyInfo, - Predicate virtualPredicate, ClassInitializerInfo classInitializerInfo) { + Predicate virtualPredicate, ClassInitializerInfo classInitializerInfo, + boolean strict) { this.debugEmitter = debugEmitter; this.initialClassSource = initialClassSource; this.classSource = classSource; @@ -81,6 +83,7 @@ public class RenderingContext { this.dependencyInfo = dependencyInfo; this.virtualPredicate = virtualPredicate; this.classInitializerInfo = classInitializerInfo; + this.strict = strict; } public ClassReaderSource getInitialClassSource() { @@ -399,6 +402,10 @@ public class RenderingContext { return holder.injector; } + public boolean isStrict() { + return strict; + } + @PlatformMarker private static boolean isBootstrap() { return false; diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java index 3f3a0d5fe..9d3fee864 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java @@ -49,6 +49,8 @@ public class RuntimeRenderer { "setStackTrace", StackTraceElement[].class, void.class); private static final MethodReference AIOOBE_INIT_METHOD = new MethodReference(ArrayIndexOutOfBoundsException.class, "", void.class); + private static final MethodReference CCE_INIT_METHOD = new MethodReference(ClassCastException.class, + "", void.class); private final ClassReaderSource classSource; private final SourceWriter writer; @@ -73,6 +75,7 @@ public class RuntimeRenderer { renderCreateStackTraceElement(); renderSetStackTrace(); renderThrowAIOOBE(); + renderThrowCCE(); } catch (IOException e) { throw new RenderingException("IO error", e); } @@ -267,4 +270,18 @@ public class RuntimeRenderer { writer.outdent().append("}").newLine(); } + + private void renderThrowCCE() throws IOException { + writer.append("function $rt_throwCCE()").ws().append("{").indent().softNewLine(); + + ClassReader cls = classSource.get(CCE_INIT_METHOD.getClassName()); + if (cls != null) { + MethodReader method = cls.getMethod(CCE_INIT_METHOD.getDescriptor()); + if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) { + writer.append("$rt_throw(").appendInit(CCE_INIT_METHOD).append("());").softNewLine(); + } + } + + writer.outdent().append("}").newLine(); + } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 9e9ba4c42..f34978a08 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -915,7 +915,41 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(CastExpr expr) { - expr.getValue().acceptVisitor(this); + if (context.isStrict()) { + try { + if (expr.getLocation() != null) { + pushLocation(expr.getLocation()); + } + + if (isClass(expr.getTarget(), context.getClassSource())) { + writer.appendFunction("$rt_castToClass"); + } else { + writer.appendFunction("$rt_castToInterface"); + } + writer.append("("); + precedence = Precedence.min(); + expr.getValue().acceptVisitor(this); + writer.append(",").ws(); + context.typeToClsString(writer, expr.getTarget()); + writer.append(")"); + if (expr.getLocation() != null) { + popLocation(); + } + } catch (IOException e) { + throw new RenderingException("IO error occurred", e); + } + } else { + expr.getValue().acceptVisitor(this); + } + } + + static boolean isClass(ValueType type, ClassReaderSource classSource) { + if (!(type instanceof ValueType.Object)) { + return false; + } + String className = ((ValueType.Object) type).getClassName(); + ClassReader cls = classSource.get(className); + return cls != null && !cls.hasModifier(ElementModifier.INTERFACE); } @Override @@ -1475,32 +1509,26 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (expr.getLocation() != null) { pushLocation(expr.getLocation()); } - if (expr.getType() instanceof ValueType.Object) { - String clsName = ((ValueType.Object) expr.getType()).getClassName(); - ClassReader cls = classSource.get(clsName); - if (cls != null && !cls.hasModifier(ElementModifier.INTERFACE)) { - boolean needsParentheses = Precedence.COMPARISON.ordinal() < precedence.ordinal(); - if (needsParentheses) { - writer.append('('); - } - precedence = Precedence.CONDITIONAL.next(); - expr.getExpr().acceptVisitor(this); - writer.append(" instanceof ").appendClass(clsName); - if (needsParentheses) { - writer.append(')'); - } - if (expr.getLocation() != null) { - popLocation(); - } - return; + if (isClass(expr.getType(), context.getClassSource())) { + boolean needsParentheses = Precedence.COMPARISON.ordinal() < precedence.ordinal(); + if (needsParentheses) { + writer.append('('); } + precedence = Precedence.CONDITIONAL.next(); + expr.getExpr().acceptVisitor(this); + writer.append(" instanceof "); + context.typeToClsString(writer, expr.getType()); + if (needsParentheses) { + writer.append(')'); + } + } else { + writer.appendFunction("$rt_isInstance").append("("); + precedence = Precedence.min(); + expr.getExpr().acceptVisitor(this); + writer.append(",").ws(); + context.typeToClsString(writer, expr.getType()); + writer.append(")"); } - writer.appendFunction("$rt_isInstance").append("("); - precedence = Precedence.min(); - expr.getExpr().acceptVisitor(this); - writer.append(",").ws(); - context.typeToClsString(writer, expr.getType()); - writer.append(")"); if (expr.getLocation() != null) { popLocation(); } diff --git a/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java b/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java index bd9c2dba8..4d7872e4d 100644 --- a/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java @@ -227,6 +227,14 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader { } } + @Override + public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { + String className = extractClassName(targetType); + if (className != null) { + getAnalyzer().linkClass(className); + } + } + @Override public void initClass(String className) { getAnalyzer().linkClass(className).initClass(getCallLocation()); diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 2bd7ac8e0..7f12b081d 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -220,6 +220,7 @@ class DependencyGraphBuilder { @Override public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { + super.cast(receiver, value, targetType); DependencyNode valueNode = nodes[value.getIndex()]; DependencyNode receiverNode = nodes[receiver.getIndex()]; ClassReaderSource classSource = dependencyAnalyzer.getClassSource(); @@ -253,6 +254,7 @@ class DependencyGraphBuilder { valueNode.connect(receiverNode); } } + @Override public void exit(VariableReader valueToReturn) { if (valueToReturn != null) { diff --git a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java index 6cf8ebebe..1327875f0 100644 --- a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java +++ b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java @@ -30,6 +30,9 @@ import org.teavm.model.Instruction; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.CastInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; @@ -41,6 +44,8 @@ public class Devirtualization { private Set readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods); private int virtualCallSites; private int directCallSites; + private int remainingCasts; + private int eliminatedCasts; public Devirtualization(DependencyInfo dependency, ClassHierarchy hierarchy) { this.dependency = dependency; @@ -55,6 +60,14 @@ public class Devirtualization { return directCallSites; } + public int getRemainingCasts() { + return remainingCasts; + } + + public int getEliminatedCasts() { + return eliminatedCasts; + } + public void apply(MethodHolder method) { MethodDependencyInfo methodDep = dependency.getMethod(method.getReference()); if (methodDep == null) { @@ -69,50 +82,10 @@ public class Devirtualization { for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); for (Instruction insn : block) { - if (!(insn instanceof InvokeInstruction)) { - continue; - } - InvokeInstruction invoke = (InvokeInstruction) insn; - if (invoke.getType() != InvocationType.VIRTUAL) { - continue; - } - ValueDependencyInfo var = methodDep.getVariable(invoke.getInstance().getIndex()); - Set implementations = getImplementations(var.getTypes(), - invoke.getMethod()); - if (implementations.size() == 1) { - MethodReference resolvedImplementaiton = implementations.iterator().next(); - if (shouldLog) { - System.out.print("DIRECT CALL " + invoke.getMethod() + " resolved to " - + resolvedImplementaiton.getClassName()); - if (insn.getLocation() != null) { - System.out.print(" at " + insn.getLocation().getFileName() + ":" - + insn.getLocation().getLine()); - } - System.out.println(); - } - invoke.setType(InvocationType.SPECIAL); - invoke.setMethod(resolvedImplementaiton); - directCallSites++; - } else { - virtualMethods.addAll(implementations); - if (shouldLog) { - System.out.print("VIRTUAL CALL " + invoke.getMethod() + " resolved to ["); - boolean first = true; - for (MethodReference impl : implementations) { - if (!first) { - System.out.print(", "); - } - first = false; - System.out.print(impl.getClassName()); - } - System.out.print("]"); - if (insn.getLocation() != null) { - System.out.print(" at " + insn.getLocation().getFileName() + ":" - + insn.getLocation().getLine()); - } - System.out.println(); - } - virtualCallSites++; + if (insn instanceof InvokeInstruction) { + applyToInvoke(methodDep, (InvokeInstruction) insn); + } else if (insn instanceof CastInstruction) { + applyToCast(methodDep, (CastInstruction) insn); } } } @@ -122,6 +95,104 @@ public class Devirtualization { } } + private void applyToInvoke(MethodDependencyInfo methodDep, InvokeInstruction invoke) { + if (invoke.getType() != InvocationType.VIRTUAL) { + return; + } + ValueDependencyInfo var = methodDep.getVariable(invoke.getInstance().getIndex()); + Set implementations = getImplementations(var.getTypes(), + invoke.getMethod()); + if (implementations.size() == 1) { + MethodReference resolvedImplementaiton = implementations.iterator().next(); + if (shouldLog) { + System.out.print("DIRECT CALL " + invoke.getMethod() + " resolved to " + + resolvedImplementaiton.getClassName()); + if (invoke.getLocation() != null) { + System.out.print(" at " + invoke.getLocation().getFileName() + ":" + + invoke.getLocation().getLine()); + } + System.out.println(); + } + invoke.setType(InvocationType.SPECIAL); + invoke.setMethod(resolvedImplementaiton); + directCallSites++; + } else { + virtualMethods.addAll(implementations); + if (shouldLog) { + System.out.print("VIRTUAL CALL " + invoke.getMethod() + " resolved to ["); + boolean first = true; + for (MethodReference impl : implementations) { + if (!first) { + System.out.print(", "); + } + first = false; + System.out.print(impl.getClassName()); + } + System.out.print("]"); + if (invoke.getLocation() != null) { + System.out.print(" at " + invoke.getLocation().getFileName() + ":" + + invoke.getLocation().getLine()); + } + System.out.println(); + } + virtualCallSites++; + } + } + + private void applyToCast(MethodDependencyInfo methodDep, CastInstruction cast) { + ValueDependencyInfo var = methodDep.getVariable(cast.getValue().getIndex()); + boolean canFail = false; + String failType = null; + for (String type : var.getTypes()) { + if (castCanFail(type, cast.getTargetType())) { + failType = type; + canFail = true; + } + } + + if (canFail) { + if (shouldLog) { + System.out.print("REMAINING CAST to " + cast.getTargetType() + " (example is " + failType + ")"); + if (cast.getLocation() != null) { + System.out.print(" at " + cast.getLocation().getFileName() + ":" + + cast.getLocation().getLine()); + } + System.out.println(); + } + remainingCasts++; + } else { + if (shouldLog) { + System.out.print("ELIMINATED CAST to " + cast.getTargetType()); + if (cast.getLocation() != null) { + System.out.print(" at " + cast.getLocation().getFileName() + ":" + + cast.getLocation().getLine()); + } + System.out.println(); + } + AssignInstruction assign = new AssignInstruction(); + assign.setAssignee(cast.getValue()); + assign.setReceiver(cast.getReceiver()); + assign.setLocation(cast.getLocation()); + cast.replace(assign); + eliminatedCasts++; + } + } + + private boolean castCanFail(String type, ValueType target) { + if (type.startsWith("[")) { + ValueType valueType = ValueType.parse(type); + if (hierarchy.isSuperType(target, valueType, false)) { + return false; + } + } else if (target instanceof ValueType.Object) { + String targetClassName = ((ValueType.Object) target).getClassName(); + if (hierarchy.isSuperType(targetClassName, type, false)) { + return false; + } + } + return true; + } + private Set getImplementations(String[] classNames, MethodReference ref) { return implementations(hierarchy, dependency, classNames, ref); } diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 2b91e1dd0..a9ebb2443 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -44,6 +44,18 @@ function $rt_isAssignable(from, to) { } return false; } +function $rt_castToInterface(obj, cls) { + if (obj !== null && !$rt_isInstance(obj, cls)) { + $rt_throwCCE(); + } + return obj; +} +function $rt_castToClass(obj, cls) { + if (obj !== null && !(obj instanceof cls)) { + $rt_throwCCE(); + } + return obj; +} Array.prototype.fill = Array.prototype.fill || function(value,start,end) { var len = this.length; if (!len) return this; diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 1997b8632..ff6b25bd9 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -247,7 +247,12 @@ class JSClassProcessor { } ClassReader targetClass = classSource.get(targetClassName); if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) { - return false; + AssignInstruction assign = new AssignInstruction(); + assign.setLocation(location.getSourceLocation()); + assign.setAssignee(cast.getValue()); + assign.setReceiver(cast.getReceiver()); + replacement.add(assign); + return true; } Variable result = marshaller.unwrapFunctor(location, cast.getValue(), targetClass); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java index e090040b7..154e878df 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSValueMarshaller.java @@ -33,7 +33,6 @@ import org.teavm.model.ReferenceCache; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; import org.teavm.model.Variable; -import org.teavm.model.instructions.CastInstruction; import org.teavm.model.instructions.ClassConstantInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; @@ -290,14 +289,7 @@ class JSValueMarshaller { } else if (className.equals("java.lang.String")) { return unwrap(var, "unwrapString", JSMethods.JS_OBJECT, stringType, location.getSourceLocation()); } else if (typeHelper.isJavaScriptClass(className)) { - Variable result = program.createVariable(); - CastInstruction castInsn = new CastInstruction(); - castInsn.setReceiver(result); - castInsn.setValue(var); - castInsn.setTargetType(type); - castInsn.setLocation(location.getSourceLocation()); - replacement.add(castInsn); - return result; + return var; } } else if (type instanceof ValueType.Array) { return unwrapArray(location, var, (ValueType.Array) type); @@ -314,14 +306,6 @@ class JSValueMarshaller { itemType = ((ValueType.Array) itemType).getItemType(); } - CastInstruction castInsn = new CastInstruction(); - castInsn.setValue(var); - castInsn.setTargetType(ValueType.parse(JSArrayReader.class)); - var = program.createVariable(); - castInsn.setReceiver(var); - castInsn.setLocation(location.getSourceLocation()); - replacement.add(castInsn); - var = degree == 1 ? unwrapSingleDimensionArray(location, var, itemType) : unwrapMultiDimensionArray(location, var, itemType, degree); @@ -477,16 +461,6 @@ class JSValueMarshaller { private Variable unwrap(Variable var, String methodName, ValueType argType, ValueType resultType, TextLocation location) { - if (!argType.isObject(JSObject.class.getName())) { - Variable castValue = program.createVariable(); - CastInstruction castInsn = new CastInstruction(); - castInsn.setValue(var); - castInsn.setReceiver(castValue); - castInsn.setLocation(location); - castInsn.setTargetType(argType); - replacement.add(castInsn); - var = castValue; - } Variable result = program.createVariable(); InvokeInstruction insn = new InvokeInstruction(); insn.setMethod(referenceCache.getCached(referenceCache.getCached(new MethodReference( diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java index 007481039..d91c788a9 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java @@ -1084,7 +1084,7 @@ public class CompositeMethodGenerator { private Variable unwrapArray(ValueType type, Variable array) { CastInstruction cast = new CastInstruction(); - cast.setTargetType(ValueType.arrayOf(type)); + cast.setTargetType(type); cast.setValue(array); cast.setReceiver(program.createVariable()); add(cast); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index a0080483c..2c328cd0b 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -35,6 +35,7 @@ class ResourceProgramTransformer { Object.class, String[].class); private static final MethodReference GET_PROPERTY = new MethodReference(ResourceAccessor.class, "getProperty", Object.class, String.class, Object.class); + private static final ValueType RESOURCE = ValueType.parse(Resource.class); private ClassHierarchy hierarchy; private Program program; @@ -59,6 +60,15 @@ class ResourceProgramTransformer { insn.insertNextAll(replacement); insn.delete(); } + } else if (insn instanceof CastInstruction) { + CastInstruction cast = (CastInstruction) insn; + if (hierarchy.isSuperType(RESOURCE, cast.getTargetType(), false)) { + AssignInstruction assign = new AssignInstruction(); + assign.setReceiver(cast.getReceiver()); + assign.setAssignee(cast.getValue()); + assign.setLocation(cast.getLocation()); + insn.replace(assign); + } } } } @@ -171,13 +181,7 @@ class ResourceProgramTransformer { return instructions; } default: { - Variable resultVar = insn.getProgram().createVariable(); - getProperty(insn, property, instructions, resultVar); - CastInstruction castInsn = new CastInstruction(); - castInsn.setReceiver(insn.getReceiver()); - castInsn.setTargetType(type); - castInsn.setValue(resultVar); - instructions.add(castInsn); + getProperty(insn, property, instructions, insn.getReceiver()); return instructions; } }