From bf68cf4b7dd5cb3fb08a9ffbc61891c798e07bf8 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 26 Mar 2014 23:15:55 +0400 Subject: [PATCH] Fixes bugs in JSO and building resources --- .../teavm/dependency/DependencyChecker.java | 5 +++ .../dependency/DependencyGraphBuilder.java | 15 +++++++ .../org/teavm/dependency/DependencyInfo.java | 2 + .../org/teavm/vm/DirectoryBuildTarget.java | 7 ++++ teavm-jso/src/main/java/org/teavm/jso/JS.java | 42 +++++++++++++++++++ .../java/org/teavm/jso/JSConstructor.java | 42 ++++--------------- .../src/main/java/org/teavm/jso/JSGlobal.java | 11 +++-- .../java/org/teavm/jso/JSNativeGenerator.java | 13 ++++++ .../jso/plugin/JavascriptNativeProcessor.java | 36 ++++++++++++---- 9 files changed, 129 insertions(+), 44 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 20d44b8cb..4258d3f08 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -120,6 +120,11 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { return classSource; } + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + @Override public String generateClassName() { return "$$tmp$$.TempClass" + classNameSuffix++; diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index a00ec8f88..b3d3f1f22 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -338,6 +338,21 @@ class DependencyGraphBuilder { @Override public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "[" + itemType)); + final String className = extractClassName(itemType); + if (className != null) { + useRunners.add(new Runnable() { + @Override public void run() { + dependencyChecker.initClass(className, callerStack); + } + }); + } + } + + private String extractClassName(ValueType itemType) { + while (itemType instanceof ValueType.Array) { + itemType = ((ValueType.Array)itemType).getItemType(); + } + return itemType instanceof ValueType.Object ? ((ValueType.Object)itemType).getClassName() : null; } @Override diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java index 396b0a284..c3b79f09e 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java @@ -27,6 +27,8 @@ import org.teavm.model.MethodReference; public interface DependencyInfo { ClassReaderSource getClassSource(); + ClassLoader getClassLoader(); + boolean isMethodAchievable(MethodReference methodRef); Collection getAchievableMethods(); diff --git a/teavm-core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java b/teavm-core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java index a7e6db74b..d9b902d15 100644 --- a/teavm-core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java +++ b/teavm-core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java @@ -33,6 +33,13 @@ public class DirectoryBuildTarget implements BuildTarget { @Override public OutputStream createResource(String fileName) throws IOException { + int index = fileName.lastIndexOf('/'); + if (index >= 0) { + File dir = new File(directory, fileName.substring(0, index)); + if (!dir.exists()) { + dir.mkdirs(); + } + } return new FileOutputStream(new File(directory, fileName)); } } diff --git a/teavm-jso/src/main/java/org/teavm/jso/JS.java b/teavm-jso/src/main/java/org/teavm/jso/JS.java index 4bf0ae9d4..693200466 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JS.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JS.java @@ -166,6 +166,48 @@ public final class JS { public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g, JSObject h); + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c, JSObject d); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c, JSObject d, JSObject e); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c, JSObject d, JSObject e, JSObject f); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c, JSObject d, JSObject e, JSObject f, JSObject g); + + @InjectedBy(JSNativeGenerator.class) + @PluggableDependency(JSNativeGenerator.class) + public static native JSObject instantiate(JSObject instance, JSObject constructor, JSObject a, JSObject b, + JSObject c, JSObject d, JSObject e, JSObject f, JSObject g, JSObject h); + public static Iterable iterate(final JSArray array) { return new Iterable() { @Override public Iterator iterator() { diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSConstructor.java b/teavm-jso/src/main/java/org/teavm/jso/JSConstructor.java index e95bbb132..256c685dd 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSConstructor.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSConstructor.java @@ -1,40 +1,16 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.teavm.jso; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** * * @author Alexey Andreev */ -public interface JSConstructor extends JSFunction { - T construct(); - - T construct(JSObject a); - - T construct(JSObject a, JSObject b); - - T construct(JSObject a, JSObject b, JSObject c); - - T construct(JSObject a, JSObject b, JSObject c, JSObject d); - - T construct(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e); - - T construct(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f); - - T construct(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g); - - T construct(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g, JSObject h); +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface JSConstructor { + String value() default ""; } diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSGlobal.java b/teavm-jso/src/main/java/org/teavm/jso/JSGlobal.java index cb3334673..797c45660 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSGlobal.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSGlobal.java @@ -20,9 +20,12 @@ package org.teavm.jso; * @author Alexey Andreev */ public interface JSGlobal extends JSObject { - @JSProperty - JSConstructor> getArray(); + @JSConstructor + JSObject newObject(); - @JSProperty - JSConstructor getObject(); + @JSConstructor + JSArray newArray(); + + @JSConstructor + JSArray newArray(int sz); } diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java index 00f5cc1a2..cb7e4b2a4 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java @@ -87,6 +87,19 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin } writer.append(')'); break; + case "instantiate": + writer.append("(new "); + context.writeExpr(context.getArgument(0)); + renderProperty(context.getArgument(1), context); + writer.append('('); + for (int i = 2; i < context.argumentCount(); ++i) { + if (i > 2) { + writer.append(',').ws(); + } + context.writeExpr(context.getArgument(i)); + } + writer.append("))"); + break; case "wrap": context.writeExpr(context.getArgument(0)); break; diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java index ecf3d8d57..73e6a0971 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java @@ -89,7 +89,7 @@ class JavascriptNativeProcessor { } } else if (isProperSetter(method.getDescriptor())) { Variable wrapped = wrap(invoke.getArguments().get(0), method.parameterType(0)); - addPropertySet(cutPrefix(method.getName(), 3), invoke.getArguments().get(0), wrapped); + addPropertySet(cutPrefix(method.getName(), 3), invoke.getInstance(), wrapped); } else { throw new RuntimeException("Method " + invoke.getMethod() + " is not " + "a proper native JavaScript property declaration"); @@ -112,25 +112,47 @@ class JavascriptNativeProcessor { "a proper native JavaScript indexer declaration"); } } else { - if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) { - throw new RuntimeException("Method " + invoke.getMethod() + " is not " + - "a proper native JavaScript method declaration"); + String name = method.getName(); + AnnotationReader constructorAnnot = method.getAnnotations().get(JSConstructor.class.getName()); + boolean isConstructor = false; + if (constructorAnnot != null) { + if (!isSupportedType(method.getResultType())) { + throw new RuntimeException("Method " + invoke.getMethod() + " is not " + + "a proper native JavaScript constructor declaration"); + } + AnnotationValue nameVal = constructorAnnot.getValue("value"); + name = nameVal != null ? constructorAnnot.getValue("value").getString() : ""; + if (name.isEmpty()) { + if (!method.getName().startsWith("new") || method.getName().length() == 3) { + throw new RuntimeException("Method " + invoke.getMethod() + " is not " + + "declared as a native JavaScript constructor, but its name does " + + "not satisfy conventions"); + } + name = method.getName().substring(3); + } + isConstructor = true; + } else { + if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) { + throw new RuntimeException("Method " + invoke.getMethod() + " is not " + + "a proper native JavaScript method declaration"); + } } for (ValueType arg : method.getParameterTypes()) { if (!isSupportedType(arg)) { throw new RuntimeException("Method " + invoke.getMethod() + " is not " + - "a proper native JavaScript method declaration"); + "a proper native JavaScript method or constructor declaration"); } } Variable result = invoke.getReceiver() != null ? program.createVariable() : null; InvokeInstruction newInvoke = new InvokeInstruction(); ValueType[] signature = new ValueType[method.parameterCount() + 3]; Arrays.fill(signature, ValueType.object(JSObject.class.getName())); - newInvoke.setMethod(new MethodReference(JS.class.getName(), "invoke", signature)); + newInvoke.setMethod(new MethodReference(JS.class.getName(), + isConstructor ? "instantiate" : "invoke", signature)); newInvoke.setType(InvocationType.SPECIAL); newInvoke.setReceiver(result); newInvoke.getArguments().add(invoke.getInstance()); - newInvoke.getArguments().add(addStringWrap(addString(method.getName()))); + newInvoke.getArguments().add(addStringWrap(addString(name))); for (int k = 0; k < invoke.getArguments().size(); ++k) { Variable arg = wrapArgument(invoke.getArguments().get(k), method.parameterType(k)); newInvoke.getArguments().add(arg);