diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java index 4d443b258..3f0514e8d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java @@ -422,7 +422,7 @@ public class TString extends TObject implements TSerializable, TComparable exportedClasses = new HashMap<>(); JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { - this.classSource = classSource; + this.classSource = new JavascriptProcessedClassSource(classSource); this.classLoader = classLoader; - dependencyChecker = new DependencyChecker(classSource, classLoader, executor); + dependencyChecker = new DependencyChecker(this.classSource, classLoader, executor); this.executor = executor; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java index 68de6d51b..0d5cb4b0d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptNativeProcessor.java @@ -15,12 +15,10 @@ */ package org.teavm.javascript; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.teavm.javascript.ni.JSObject; +import java.util.*; +import org.teavm.javascript.ni.*; import org.teavm.model.*; -import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.*; /** * @@ -29,6 +27,8 @@ import org.teavm.model.instructions.InvokeInstruction; class JavascriptNativeProcessor { private ClassHolderSource classSource; private Map knownJavaScriptClasses = new HashMap<>(); + private Program program; + private List replacement = new ArrayList<>(); public JavascriptNativeProcessor(ClassHolderSource classSource) { this.classSource = classSource; @@ -36,6 +36,7 @@ class JavascriptNativeProcessor { } public void processProgram(Program program) { + this.program = program; for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); List instructions = block.getInstructions(); @@ -44,10 +45,219 @@ class JavascriptNativeProcessor { if (!(insn instanceof InvokeInstruction)) { continue; } + InvokeInstruction invoke = (InvokeInstruction)insn; + if (!isJavaScriptClass(invoke.getMethod().getClassName())) { + continue; + } + replacement.clear(); + MethodHolder method = getMethod(invoke.getMethod()); + if (method.getAnnotations().get(JSProperty.class.getName()) != null) { + if (isProperGetter(method.getDescriptor())) { + String propertyName = method.getName().charAt(0) == 'i' ? cutPrefix(method.getName(), 2) : + cutPrefix(method.getName(), 3); + Variable result = invoke.getReceiver() != null ? program.createVariable() : null; + addPropertyGet(propertyName, invoke.getInstance(), result); + if (result != null) { + result = unwrap(result, method.getResultType()); + copyVar(result, invoke.getReceiver()); + } + } else if (isProperSetter(method.getDescriptor())) { + Variable wrapped = wrap(invoke.getArguments().get(3), method.parameterType(2)); + addPropertySet(cutPrefix(method.getName(), 3), invoke.getArguments().get(0), wrapped); + } else { + throw new RuntimeException("Method " + invoke.getMethod() + " is not " + + "a proper native JavaScript property declaration"); + } + } else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) { + if (isProperGetIndexer(method.getDescriptor())) { + Variable result = invoke.getReceiver() != null ? program.createVariable() : null; + addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0), + method.parameterType(0)), result); + if (result != null) { + result = unwrap(result, method.getResultType()); + copyVar(result, invoke.getReceiver()); + } + } else if (isProperSetIndexer(method.getDescriptor())) { + Variable index = wrap(invoke.getArguments().get(0), method.parameterType(0)); + Variable value = wrap(invoke.getArguments().get(1), method.parameterType(1)); + addIndexerSet(invoke.getInstance(), index, value); + } else { + throw new RuntimeException("Method " + invoke.getMethod() + " is not " + + "a proper native JavaScript indexer declaration"); + } + } else { + if (!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"); + } + } + 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(), new MethodDescriptor("invoke", + signature))); + newInvoke.setType(InvocationType.SPECIAL); + newInvoke.setReceiver(result); + newInvoke.getArguments().add(invoke.getInstance()); + newInvoke.getArguments().add(addStringWrap(addString(method.getName()))); + for (int k = 0; k < invoke.getArguments().size(); ++k) { + Variable arg = wrap(invoke.getArguments().get(k), method.parameterType(k)); + newInvoke.getArguments().add(arg); + } + replacement.add(newInvoke); + if (result != null) { + result = unwrap(result, method.getResultType()); + copyVar(result, invoke.getReceiver()); + } + } + block.getInstructions().set(j, replacement.get(0)); + block.getInstructions().addAll(j + 1, replacement.subList(1, replacement.size())); + j += replacement.size() - 1; } } } + private void addPropertyGet(String propertyName, Variable instance, Variable receiver) { + Variable nameVar = addStringWrap(addString(propertyName)); + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor("get", + ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), + ValueType.object(JSObject.class.getName())))); + insn.setReceiver(receiver); + insn.getArguments().add(instance); + insn.getArguments().add(nameVar); + replacement.add(insn); + } + + private void addPropertySet(String propertyName, Variable instance, Variable value) { + Variable nameVar = addStringWrap(addString(propertyName)); + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor("set", + ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), + ValueType.VOID))); + insn.getArguments().add(instance); + insn.getArguments().add(nameVar); + insn.getArguments().add(value); + replacement.add(insn); + } + + private void addIndexerGet(Variable array, Variable index, Variable receiver) { + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor("get", + ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), + ValueType.object(JSObject.class.getName())))); + insn.setReceiver(receiver); + insn.getArguments().add(array); + insn.getArguments().add(index); + replacement.add(insn); + } + + private void addIndexerSet(Variable array, Variable index, Variable value) { + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor("set", + ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), + ValueType.object(JSObject.class.getName()), ValueType.VOID))); + insn.getArguments().add(array); + insn.getArguments().add(index); + insn.getArguments().add(value); + replacement.add(insn); + } + + private void copyVar(Variable a, Variable b) { + AssignInstruction insn = new AssignInstruction(); + insn.setAssignee(a); + insn.setReceiver(b); + replacement.add(insn); + } + + private Variable addStringWrap(Variable var) { + return wrap(var, ValueType.object("java.lang.String")); + } + + private Variable addString(String str) { + Variable var = program.createVariable(); + StringConstantInstruction nameInsn = new StringConstantInstruction(); + nameInsn.setReceiver(var); + nameInsn.setConstant(str); + replacement.add(nameInsn); + return var; + } + + private Variable unwrap(Variable var, ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + return unwrap(var, "unwrapBoolean", ValueType.BOOLEAN); + case BYTE: + return unwrap(var, "unwrapByte", ValueType.BYTE); + case SHORT: + return unwrap(var, "unwrapShort", ValueType.SHORT); + case INTEGER: + return unwrap(var, "unwrapShort", ValueType.INTEGER); + case CHARACTER: + return unwrap(var, "unwrapCharacter", ValueType.CHARACTER); + case DOUBLE: + return unwrap(var, "unwrapDouble", ValueType.DOUBLE); + case FLOAT: + return unwrap(var, "unwrapFloat", ValueType.FLOAT); + case LONG: + break; + } + } else if (type instanceof ValueType.Object) { + String className = ((ValueType.Object)type).getClassName(); + if (className.equals(JSObject.class.getName())) { + return var; + } else if (className.equals("java.lang.String")) { + return unwrap(var, "unwrapString", ValueType.object("java.lang.String")); + } else { + Variable result = program.createVariable(); + CastInstruction castInsn = new CastInstruction(); + castInsn.setReceiver(result); + castInsn.setValue(var); + castInsn.setTargetType(type); + replacement.add(castInsn); + return result; + } + } + throw new IllegalArgumentException("Unsupported type: " + type); + } + + private Variable unwrap(Variable var, String methodName, ValueType resultType) { + Variable result = program.createVariable(); + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor(methodName, + resultType))); + insn.getArguments().add(var); + insn.setReceiver(result); + insn.setType(InvocationType.SPECIAL); + replacement.add(insn); + return result; + } + + private Variable wrap(Variable var, ValueType type) { + if (type instanceof ValueType.Object) { + String className = ((ValueType.Object)type).getClassName(); + if (!className.equals("java.lang.String")) { + return var; + } + } + Variable result = program.createVariable(); + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), new MethodDescriptor("wrap", type, + ValueType.object(JSObject.class.getName())))); + insn.getArguments().add(var); + insn.setReceiver(result); + insn.setType(InvocationType.SPECIAL); + replacement.add(insn); + return result; + } + private boolean isJavaScriptClass(String className) { Boolean known = knownJavaScriptClasses.get(className); if (known == null) { @@ -69,4 +279,89 @@ class JavascriptNativeProcessor { } return false; } + + private MethodHolder getMethod(MethodReference ref) { + ClassHolder cls = classSource.getClassHolder(ref.getClassName()); + MethodHolder method = cls.getMethod(ref.getDescriptor()); + if (method != null) { + return method; + } + for (String iface : cls.getInterfaces()) { + method = getMethod(new MethodReference(iface, ref.getDescriptor())); + if (method != null) { + return method; + } + } + return null; + } + + private boolean isProperGetter(MethodDescriptor desc) { + if (desc.parameterCount() > 0 || !isSupportedType(desc.getResultType())) { + return false; + } + if (desc.getResultType().equals(ValueType.BOOLEAN)) { + if (isProperPrefix(desc.getName(), "is")) { + return true; + } + } + return isProperPrefix(desc.getName(), "get"); + } + + private boolean isProperSetter(MethodDescriptor desc) { + if (desc.parameterCount() != 1 || !isSupportedType(desc.parameterType(0)) || + desc.getResultType() != ValueType.VOID) { + return false; + } + return isProperPrefix(desc.getName(), "set"); + } + + private boolean isProperPrefix(String name, String prefix) { + if (!name.startsWith(prefix) || name.length() == prefix.length()) { + return false; + } + char c = name.charAt(prefix.length()); + return Character.isUpperCase(c); + } + + private boolean isProperGetIndexer(MethodDescriptor desc) { + return desc.parameterCount() == 1 && isSupportedType(desc.parameterType(0)) && + isSupportedType(desc.getResultType()); + } + + private boolean isProperSetIndexer(MethodDescriptor desc) { + return desc.parameterCount() == 2 && isSupportedType(desc.parameterType(0)) && + isSupportedType(desc.parameterType(0)) && desc.getResultType() == ValueType.VOID; + } + + private String cutPrefix(String name, int prefixLength) { + if (name.length() == prefixLength + 1) { + return name.substring(prefixLength).toLowerCase(); + } + char c = name.charAt(prefixLength + 1); + if (Character.isUpperCase(c)) { + return name.substring(prefixLength); + } + return Character.toLowerCase(name.charAt(prefixLength)) + name.substring(prefixLength + 1); + } + + private boolean isSupportedType(ValueType type) { + if (type == ValueType.VOID) { + return false; + } + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case LONG: + return false; + default: + return true; + } + } else if (type instanceof ValueType.Array) { + return isSupportedType(((ValueType.Array)type).getItemType()); + } else if (type instanceof ValueType.Object) { + String typeName = ((ValueType.Object)type).getClassName(); + return typeName.equals("java.lang.String") || isJavaScriptClass(typeName); + } else { + return false; + } + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java new file mode 100644 index 000000000..6321d9ec1 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptProcessedClassSource.java @@ -0,0 +1,50 @@ +/* + * 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.javascript; + +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.MethodHolder; + +/** + * + * @author Alexey Andreev + */ +public class JavascriptProcessedClassSource implements ClassHolderSource { + private ClassHolderSource innerSource; + + public JavascriptProcessedClassSource(ClassHolderSource innerSource) { + this.innerSource = innerSource; + } + + @Override + public ClassHolder getClassHolder(String name) { + ClassHolder cls = innerSource.getClassHolder(name); + if (cls != null) { + transformClass(cls); + } + return cls; + } + + private void transformClass(ClassHolder cls) { + JavascriptNativeProcessor processor = new JavascriptNativeProcessor(innerSource); + for (MethodHolder method : cls.getMethods()) { + if (method.getProgram() != null) { + processor.processProgram(method.getProgram()); + } + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java new file mode 100644 index 000000000..40b1e983d --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JS.java @@ -0,0 +1,178 @@ +/* + * 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.javascript.ni; + +import java.util.Iterator; + +/** + * + * @author Alexey Andreev + */ +public final class JS { + private JS() { + } + + public static JSType getType(JSObject obj) { + switch (unwrapString(getTypeName(obj))) { + case "boolean": + return JSType.OBJECT; + case "number": + return JSType.NUMBER; + case "string": + return JSType.STRING; + case "function": + return JSType.FUNCTION; + case "object": + return JSType.OBJECT; + case "undefined": + return JSType.UNDEFINED; + } + throw new AssertionError("Unexpected type"); + } + + public static native JSArray createArray(int size); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject getTypeName(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject getGlobal(); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject wrap(String str); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject wrap(char c); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject wrap(int num); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject wrap(float num); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject wrap(double num); + + public static JSArray wrap(T[] array) { + JSArray result = createArray(array.length); + for (int i = 0; i < array.length; ++i) { + result.set(i, array[i]); + } + return result; + } + + public static JSArray> wrap(T[][] array) { + JSArray> result = createArray(array.length); + for (int i = 0; i < array.length; ++i) { + result.set(i, wrap(array[i])); + } + return result; + } + + public static JSArray>> wrap(T[][][] array) { + JSArray>> result = createArray(array.length); + for (int i = 0; i < array.length; ++i) { + result.set(i, wrap(array[i])); + } + return result; + } + + @GeneratedBy(JSNativeGenerator.class) + public static native boolean unwrapBoolean(JSObject obj); + + public static byte unwrapByte(JSObject obj) { + return (byte)unwrapInt(obj); + } + + public static short unwrapShort(JSObject obj) { + return (short)unwrapInt(obj); + } + + @GeneratedBy(JSNativeGenerator.class) + public static native int unwrapInt(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native float unwrapFloat(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native double unwrapDouble(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native String unwrapString(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native char unwrapCharacter(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native boolean isUndefined(JSObject obj); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, + JSObject d); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, + JSObject d, JSObject e); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, + JSObject d, JSObject e, JSObject f); + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c, + JSObject d, JSObject e, JSObject f, JSObject g); + + @GeneratedBy(JSNativeGenerator.class) + 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); + + public static Iterable iterate(final JSArray array) { + return new Iterable() { + @Override public Iterator iterator() { + return new Iterator() { + int index = 0; + @Override public boolean hasNext() { + return index < array.getLength(); + } + @Override public T next() { + return array.get(index++); + } + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + @GeneratedBy(JSNativeGenerator.class) + public static native JSObject get(JSObject instance, JSObject index); + + @GeneratedBy(JSNativeGenerator.class) + public static native void set(JSObject instance, JSObject index, JSObject obj); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSArray.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSArray.java index 66621b08b..21c06ed45 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSArray.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSArray.java @@ -19,12 +19,13 @@ package org.teavm.javascript.ni; * * @author Alexey Andreev */ -public interface JSArray extends JSObject, Iterable { +public interface JSArray extends JSObject { @JSProperty int getLength(); - @Override + @JSIndexer T get(int index); - JSArray cast(); + @JSIndexer + void set(int index, T value); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSFunction.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSFunction.java index 5ae2343cb..873382735 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSFunction.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSFunction.java @@ -24,7 +24,7 @@ public interface JSFunction extends JSObject { int getLength(); @JSProperty - JSString getName(); + String getName(); JSObject call(JSObject thisArg); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSString.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSIndexer.java similarity index 72% rename from teavm-core/src/main/java/org/teavm/javascript/ni/JSString.java rename to teavm-core/src/main/java/org/teavm/javascript/ni/JSIndexer.java index a69a0a94d..0b13c18ce 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSString.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSIndexer.java @@ -15,10 +15,16 @@ */ package org.teavm.javascript.ni; +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 JSString extends JSObject { - +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface JSIndexer { } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSRootNativeGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java similarity index 55% rename from teavm-core/src/main/java/org/teavm/javascript/ni/JSRootNativeGenerator.java rename to teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java index 64deedf66..7275f5739 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSRootNativeGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java @@ -24,7 +24,7 @@ import org.teavm.model.MethodReference; * * @author Alexey Andreev */ -public class JSRootNativeGenerator implements Generator { +public class JSNativeGenerator implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { @@ -42,17 +42,53 @@ public class JSRootNativeGenerator implements Generator { writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine(); } break; + case "get": + writer.append("return ").append(context.getParameterName(1)).append("[") + .append(context.getParameterName(2)).append("];").softNewLine(); + break; + case "set": + writer.append(context.getParameterName(1)).append("[").append(context.getParameterName(2)) + .append("] = ").append(context.getParameterName(3)).softNewLine(); + break; + case "invoke": + generateInvoke(context, writer, methodRef.parameterCount() - 2); + break; + case "isUndefined": + writer.append("return ").append(context.getParameterName(1)).append(" === undefined;"); + break; + default: + if (methodRef.getName().startsWith("unwrap")) { + if (methodRef.getDescriptor().getResultType().isObject("java.lang.String")) { + writer.append("return $rt_str(").append(context.getParameterName(1)).append(");") + .softNewLine(); + } else { + writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine(); + } + } + break; } } private void generateWrapString(GeneratorContext context, SourceWriter writer) throws IOException { FieldReference charsField = new FieldReference("java.lang.String", "characters"); writer.append("var result = \"\";").softNewLine(); - writer.append("var data = ").append(context.getParameterName(1)) + writer.append("var data = ").append(context.getParameterName(1)).append('.') .appendField(charsField).append(".data;").softNewLine(); writer.append("for (var i = 0; i < data.length; i = (i + 1) | 0) {").indent().softNewLine(); writer.append("result += String.fromCharCode(data[i]);").softNewLine(); writer.outdent().append("}").softNewLine(); writer.append("return result;").softNewLine(); } + + private void generateInvoke(GeneratorContext context, SourceWriter writer, int argNum) throws IOException { + writer.append("return ").append(context.getParameterName(1)).append("[") + .append(context.getParameterName(2)).append("]("); + for (int i = 0; i < argNum; ++i) { + if (i > 0) { + writer.append(",").ws(); + } + writer.append(context.getParameterName(i + 3)); + } + writer.append(");").softNewLine(); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSObject.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSObject.java index 3ba0b3144..63a2428a5 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSObject.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/JSObject.java @@ -20,78 +20,6 @@ package org.teavm.javascript.ni; * @author Alexey Andreev */ public interface JSObject { - @JSProperty - JSObject getPrototype(); - - JSObject get(String name); - - void set(String name, JSObject obj); - - JSObject get(JSString name); - - void set(JSString name, JSObject obj); - - JSObject get(int index); - - void set(int index, JSObject obj); - - JSObject invoke(String method); - - JSObject invoke(String method, JSObject a); - - JSObject invoke(String method, JSObject a, JSObject b); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c, JSObject d); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, JSObject g); - - JSObject invoke(String method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, - JSObject g, JSObject h); - - JSObject invoke(JSString method); - - JSObject invoke(JSString method, JSObject a); - - JSObject invoke(JSString method, JSObject a, JSObject b); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c, JSObject d); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, - JSObject g); - - JSObject invoke(JSString method, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f, - JSObject g, JSObject h); - - @JSProperty - boolean hasOwnProperty(String property); - - @JSProperty - boolean hasOwnProperty(JSString property); - - boolean asBoolean(); - - int asInteger(); - - double asNumber(); - - String asString(); - - boolean isNull(); - - boolean isUndefined(); - - @JSMethod("toString") - JSString toJSString(); + @Override + String toString(); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/JSRoot.java b/teavm-core/src/main/java/org/teavm/javascript/ni/JSRoot.java deleted file mode 100644 index 0700a55d8..000000000 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/JSRoot.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.javascript.ni; - -/** - * - * @author Alexey Andreev - */ -public final class JSRoot { - private JSRoot() { - } - - public static JSType getType(JSObject obj) { - switch (getTypeName(obj).asString()) { - case "boolean": - return JSType.OBJECT; - case "number": - return JSType.NUMBER; - case "string": - return JSType.STRING; - case "function": - return JSType.FUNCTION; - case "object": - return JSType.OBJECT; - case "undefined": - return JSType.UNDEFINED; - } - throw new AssertionError("Unexpected type"); - } - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSString getTypeName(JSObject obj); - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSObject getGlobal(); - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSString wrap(String str); - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSNumber wrap(int num); - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSNumber wrap(float num); - - @GeneratedBy(JSRootNativeGenerator.class) - public static native JSNumber wrap(double num); -} diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml index 3abc6a52d..44aef862b 100644 --- a/teavm-samples/pom.xml +++ b/teavm-samples/pom.xml @@ -23,6 +23,19 @@ teavm-samples + + + org.teavm + teavm-core + 0.0.1-SNAPSHOT + + + org.teavm + teavm-dom + 0.0.1-SNAPSHOT + + + diff --git a/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java b/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java index ce4f9036f..97eeefcbc 100644 --- a/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java +++ b/teavm-samples/src/main/java/org/teavm/samples/HelloWorld.java @@ -15,22 +15,48 @@ */ package org.teavm.samples; +import org.teavm.dom.core.Document; +import org.teavm.dom.core.Element; +import org.teavm.dom.core.Window; +import org.teavm.javascript.ni.JS; + + /** * * @author Alexey Andreev */ public class HelloWorld { + private static Window window; + private static Document document; + private static Element body; + public static void main(String[] args) { - System.out.println("Hello, world!"); - System.out.println("Here is the Fibonacci sequence:"); + window = (Window)JS.getGlobal(); + document = window.getDocument(); + body = document.getDocumentElement().getElementsByTagName("body").item(0); + + println("Hello, world!"); + println("Here is the Fibonacci sequence:"); long a = 0; long b = 1; for (int i = 0; i < 70; ++i) { - System.out.println(a); + println(a); long c = a + b; a = b; b = c; } - System.out.println("And so on..."); + println("And so on..."); + } + + private static void println(Object obj) { + Element elem = document.createElement("div"); + elem.appendChild(document.createTextNode(String.valueOf(obj))); + body.appendChild(elem); + } + + private static void println(long val) { + Element elem = document.createElement("div"); + elem.appendChild(document.createTextNode(String.valueOf(val))); + body.appendChild(elem); } }