mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Adds support of native JavaScript interaction
This commit is contained in:
parent
b621b0524b
commit
ae2e669ec3
|
@ -422,7 +422,7 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
}
|
||||
|
||||
public static TString valueOf(TObject obj) {
|
||||
return obj != null ? obj.toString0() : TString.wrap("null");
|
||||
return obj != null ? TString.wrap(obj.toString()) : TString.wrap("null");
|
||||
}
|
||||
|
||||
public static TString valueOf(char[] data) {
|
||||
|
@ -450,19 +450,19 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
}
|
||||
|
||||
public static TString valueOf(int i) {
|
||||
return new TStringBuilder().append(i).toString0();
|
||||
return TString.wrap(new TStringBuilder().append(i).toString());
|
||||
}
|
||||
|
||||
public static TString valueOf(long l) {
|
||||
return new TStringBuilder().append(l).toString0();
|
||||
return TString.wrap(new TStringBuilder().append(l).toString());
|
||||
}
|
||||
|
||||
public static TString valueOf(float f) {
|
||||
return new TStringBuilder().append(f).toString0();
|
||||
return TString.wrap(new TStringBuilder().append(f).toString());
|
||||
}
|
||||
|
||||
public static TString valueOf(double d) {
|
||||
return new TStringBuilder().append(d).toString0();
|
||||
return TString.wrap(new TStringBuilder().append(d).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,11 +17,9 @@ package org.teavm.dependency;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.*;
|
||||
import org.teavm.common.ConcurrentCachedMapper;
|
||||
import org.teavm.common.FiniteExecutor;
|
||||
import org.teavm.common.Mapper;
|
||||
import org.teavm.common.SimpleFiniteExecutor;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.teavm.common.*;
|
||||
import org.teavm.common.ConcurrentCachedMapper.KeyListener;
|
||||
import org.teavm.model.*;
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ public class JavascriptBuilder {
|
|||
private Map<String, String> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, Boolean> knownJavaScriptClasses = new HashMap<>();
|
||||
private Program program;
|
||||
private List<Instruction> 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<Instruction> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
178
teavm-core/src/main/java/org/teavm/javascript/ni/JS.java
Normal file
178
teavm-core/src/main/java/org/teavm/javascript/ni/JS.java
Normal file
|
@ -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 <T extends JSObject> JSArray<T> 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 <T extends JSObject> JSArray<T> wrap(T[] array) {
|
||||
JSArray<T> result = createArray(array.length);
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
result.set(i, array[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends JSObject> JSArray<JSArray<T>> wrap(T[][] array) {
|
||||
JSArray<JSArray<T>> result = createArray(array.length);
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
result.set(i, wrap(array[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends JSObject> JSArray<JSArray<JSArray<T>>> wrap(T[][][] array) {
|
||||
JSArray<JSArray<JSArray<T>>> 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 <T extends JSObject> Iterable<T> iterate(final JSArray<T> array) {
|
||||
return new Iterable<T>() {
|
||||
@Override public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
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);
|
||||
}
|
|
@ -19,12 +19,13 @@ package org.teavm.javascript.ni;
|
|||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface JSArray<T extends JSObject> extends JSObject, Iterable<T> {
|
||||
public interface JSArray<T extends JSObject> extends JSObject {
|
||||
@JSProperty
|
||||
int getLength();
|
||||
|
||||
@Override
|
||||
@JSIndexer
|
||||
T get(int index);
|
||||
|
||||
<S extends T> JSArray<S> cast();
|
||||
@JSIndexer
|
||||
void set(int index, T value);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public interface JSFunction extends JSObject {
|
|||
int getLength();
|
||||
|
||||
@JSProperty
|
||||
JSString getName();
|
||||
String getName();
|
||||
|
||||
JSObject call(JSObject thisArg);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -23,6 +23,19 @@
|
|||
</parent>
|
||||
<artifactId>teavm-samples</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-core</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-dom</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user