From 30d601e8076e082c7f9f15300808d771f4132599 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 4 Apr 2015 18:29:18 +0300 Subject: [PATCH] Fix IndexedDB wrappers --- .../resources/org/teavm/javascript/runtime.js | 6 +- .../teavm/dom/indexeddb/IDBCursorSource.java | 15 +++ .../org/teavm/dom/indexeddb/IDBIndex.java | 2 +- .../org/teavm/dom/indexeddb/IDBKeyRange.java | 15 +++ .../teavm/dom/indexeddb/IDBObjectStore.java | 2 +- .../indexeddb/IDBObjectStoreParameters.java | 2 +- teavm-jso/src/main/java/org/teavm/jso/JS.java | 10 +- .../jso/plugin/JavascriptNativeProcessor.java | 125 ++++++++++++++++-- .../java/org/teavm/samples/hello/Client.java | 6 +- .../java/org/teavm/samples/video/Player.java | 58 ++++---- 10 files changed, 188 insertions(+), 53 deletions(-) diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index b3629bfb5..142d1cc95 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -473,7 +473,11 @@ TeaVMThread.prototype.start = function(callback) { throw new Error("Another thread is running"); } this.status = 0; - this.completeCallback = callback ? callback : function() {}; + this.completeCallback = callback ? callback : function(result) { + if (result instanceof Error) { + throw result; + } + }; this.run(); } TeaVMThread.prototype.resume = function() { diff --git a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBCursorSource.java b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBCursorSource.java index 10cc7884b..343d4b979 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBCursorSource.java +++ b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBCursorSource.java @@ -1,3 +1,18 @@ +/* + * Copyright 2015 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.dom.indexeddb; import org.teavm.jso.JSObject; diff --git a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBIndex.java b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBIndex.java index 379e9ad9f..e568074a5 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBIndex.java +++ b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBIndex.java @@ -37,7 +37,7 @@ public abstract class IDBIndex implements JSObject, IDBCursorSource { if (JS.getType(result) == JSType.STRING) { return new String[] { JS.unwrapString(result) }; } else { - return JS.unwrapArray((JSStringArray)result); + return JS.unwrapStringArray((JSStringArray)result); } } diff --git a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBKeyRange.java b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBKeyRange.java index cc835fb7b..8ebbd4f69 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBKeyRange.java +++ b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBKeyRange.java @@ -1,3 +1,18 @@ +/* + * Copyright 2015 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.dom.indexeddb; import org.teavm.jso.JSBody; diff --git a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStore.java b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStore.java index 6521b3613..8d6549fce 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStore.java +++ b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStore.java @@ -33,7 +33,7 @@ public abstract class IDBObjectStore implements JSObject, IDBCursorSource { if (JS.getType(result) == JSType.STRING) { return new String[] { JS.unwrapString(result) }; } else { - return JS.unwrapArray((JSStringArray)result); + return JS.unwrapStringArray((JSStringArray)result); } } diff --git a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStoreParameters.java b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStoreParameters.java index a25ea5141..0761b559d 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStoreParameters.java +++ b/teavm-dom/src/main/java/org/teavm/dom/indexeddb/IDBObjectStoreParameters.java @@ -24,7 +24,7 @@ import org.teavm.jso.JSProperty; * @author Alexey Andreev */ public abstract class IDBObjectStoreParameters implements JSObject { - @JSBody(params = "", script = "return {};") + @JSBody(params = {}, script = "return {};") public static native IDBObjectStoreParameters create(); public final IDBObjectStoreParameters keyPath(String... keys) { 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 4811fe1fa..fddc36eff 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JS.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JS.java @@ -376,7 +376,7 @@ public final class JS { return result; } - public static String[] unwrapArray(JSStringArray array) { + public static String[] unwrapStringArray(JSStringArray array) { String[] result = new String[array.getLength()]; for (int i = 0; i < result.length; ++i) { result[i] = array.get(i); @@ -384,18 +384,18 @@ public final class JS { return result; } - public static String[][] unwrapArray2(JSArray array) { + public static String[][] unwrapStringArray2(JSArray array) { String[][] result = new String[array.getLength()][]; for (int i = 0; i < result.length; ++i) { - result[i] = unwrapArray(array.get(i)); + result[i] = unwrapStringArray(array.get(i)); } return result; } - public static String[][][] unwrapArray3(JSArray> array) { + public static String[][][] unwrapStringArray3(JSArray> array) { String[][][] result = new String[array.getLength()][][]; for (int i = 0; i < result.length; ++i) { - result[i] = unwrapArray2(array.get(i)); + result[i] = unwrapStringArray2(array.get(i)); } return result; } 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 76e04f012..f8915f76b 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 @@ -502,19 +502,26 @@ class JavascriptNativeProcessor { if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive)type).getKind()) { case BOOLEAN: - return unwrap(var, "unwrapBoolean", ValueType.BOOLEAN, location.getSourceLocation()); + return unwrap(var, "unwrapBoolean", ValueType.parse(JSObject.class), ValueType.BOOLEAN, + location.getSourceLocation()); case BYTE: - return unwrap(var, "unwrapByte", ValueType.BYTE, location.getSourceLocation()); + return unwrap(var, "unwrapByte", ValueType.parse(JSObject.class), ValueType.BYTE, + location.getSourceLocation()); case SHORT: - return unwrap(var, "unwrapShort", ValueType.SHORT, location.getSourceLocation()); + return unwrap(var, "unwrapShort", ValueType.parse(JSObject.class), ValueType.SHORT, + location.getSourceLocation()); case INTEGER: - return unwrap(var, "unwrapInt", ValueType.INTEGER, location.getSourceLocation()); + return unwrap(var, "unwrapInt", ValueType.parse(JSObject.class), ValueType.INTEGER, + location.getSourceLocation()); case CHARACTER: - return unwrap(var, "unwrapCharacter", ValueType.CHARACTER, location.getSourceLocation()); + return unwrap(var, "unwrapCharacter", ValueType.parse(JSObject.class), ValueType.CHARACTER, + location.getSourceLocation()); case DOUBLE: - return unwrap(var, "unwrapDouble", ValueType.DOUBLE, location.getSourceLocation()); + return unwrap(var, "unwrapDouble", ValueType.parse(JSObject.class), ValueType.DOUBLE, + location.getSourceLocation()); case FLOAT: - return unwrap(var, "unwrapFloat", ValueType.FLOAT, location.getSourceLocation()); + return unwrap(var, "unwrapFloat", ValueType.parse(JSObject.class), ValueType.FLOAT, + location.getSourceLocation()); case LONG: break; } @@ -523,8 +530,9 @@ class JavascriptNativeProcessor { if (className.equals(JSObject.class.getName())) { return var; } else if (className.equals("java.lang.String")) { - return unwrap(var, "unwrapString", ValueType.object("java.lang.String"), location.getSourceLocation()); - } else { + return unwrap(var, "unwrapString", ValueType.parse(JSObject.class), ValueType.parse(String.class), + location.getSourceLocation()); + } else if (isNative(className)) { Variable result = program.createVariable(); CastInstruction castInsn = new CastInstruction(); castInsn.setReceiver(result); @@ -534,16 +542,58 @@ class JavascriptNativeProcessor { replacement.add(castInsn); return result; } + } else if (type instanceof ValueType.Array) { + return unwrapArray(location, var, (ValueType.Array)type); } diagnostics.error(location, "Unsupported type: {{t0}}", type); return var; } - private Variable unwrap(Variable var, String methodName, ValueType resultType, InstructionLocation location) { + private Variable unwrapArray(CallLocation location, Variable var, ValueType.Array type) { + ValueType itemType = type; + int degree = 0; + while (itemType instanceof ValueType.Array) { + ++degree; + itemType = ((ValueType.Array)itemType).getItemType(); + } + if (degree > 3) { + diagnostics.error(location, "Unsupported type: {{t0}}", type); + return var; + } + + if (itemType instanceof ValueType.Object) { + String className = ((ValueType.Object)itemType).getClassName(); + if (className.equals("java.lang.String")) { + String methodName = "unwrapStringArray"; + if (degree > 1) { + methodName += degree; + } + ValueType argType = degree == 1 ? ValueType.parse(JSStringArray.class) : + ValueType.parse(JSArray.class); + return unwrap(var, methodName, argType, type, location.getSourceLocation()); + } else if (isNative(className)) { + return unwrapObjectArray(location, var, degree, itemType, type); + } + } + diagnostics.error(location, "Unsupported type: {{t0}}", type); + return var; + } + + private Variable unwrap(Variable var, String methodName, ValueType argType, ValueType resultType, + InstructionLocation 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(new MethodReference(JS.class.getName(), methodName, ValueType.object(JSObject.class.getName()), - resultType)); + insn.setMethod(new MethodReference(JS.class.getName(), methodName, argType, resultType)); insn.getArguments().add(var); insn.setReceiver(result); insn.setType(InvocationType.SPECIAL); @@ -552,6 +602,57 @@ class JavascriptNativeProcessor { return result; } + private Variable unwrapObjectArray(CallLocation location, Variable var, int degree, ValueType itemType, + ValueType expectedType) { + String methodName = "unwrapArray"; + if (degree > 1) { + methodName += degree; + } + ValueType resultType = ValueType.parse(JSObject.class); + for (int i = 0; i < degree; ++i) { + resultType = ValueType.arrayOf(resultType); + } + + Variable classVar = program.createVariable(); + ClassConstantInstruction classInsn = new ClassConstantInstruction(); + classInsn.setConstant(itemType); + classInsn.setReceiver(classVar); + classInsn.setLocation(location.getSourceLocation()); + replacement.add(classInsn); + + Variable castValue = program.createVariable(); + CastInstruction castInsn = new CastInstruction(); + castInsn.setValue(var); + castInsn.setReceiver(castValue); + castInsn.setLocation(location.getSourceLocation()); + castInsn.setTargetType(ValueType.parse(JSArray.class)); + replacement.add(castInsn); + var = castValue; + + Variable result = program.createVariable(); + InvokeInstruction insn = new InvokeInstruction(); + insn.setMethod(new MethodReference(JS.class.getName(), methodName, ValueType.parse(Class.class), + ValueType.parse(JSArray.class), resultType)); + insn.getArguments().add(classVar); + insn.getArguments().add(var); + insn.setReceiver(result); + insn.setType(InvocationType.SPECIAL); + insn.setLocation(location.getSourceLocation()); + replacement.add(insn); + var = result; + + Variable castResult = program.createVariable(); + castInsn = new CastInstruction(); + castInsn.setValue(var); + castInsn.setReceiver(castResult); + castInsn.setLocation(location.getSourceLocation()); + castInsn.setTargetType(expectedType); + replacement.add(castInsn); + var = castResult; + + return var; + } + private Variable wrapArgument(CallLocation location, Variable var, ValueType type) { if (type instanceof ValueType.Object) { String className = ((ValueType.Object)type).getClassName(); diff --git a/teavm-samples/teavm-samples-hello/src/main/java/org/teavm/samples/hello/Client.java b/teavm-samples/teavm-samples-hello/src/main/java/org/teavm/samples/hello/Client.java index 425bac1b2..304d31e50 100644 --- a/teavm-samples/teavm-samples-hello/src/main/java/org/teavm/samples/hello/Client.java +++ b/teavm-samples/teavm-samples-hello/src/main/java/org/teavm/samples/hello/Client.java @@ -18,8 +18,8 @@ package org.teavm.samples.hello; import org.teavm.dom.ajax.ReadyStateChangeHandler; import org.teavm.dom.ajax.XMLHttpRequest; import org.teavm.dom.browser.Window; -import org.teavm.dom.events.Event; import org.teavm.dom.events.EventListener; +import org.teavm.dom.events.MouseEvent; import org.teavm.dom.html.HTMLButtonElement; import org.teavm.dom.html.HTMLDocument; import org.teavm.dom.html.HTMLElement; @@ -36,8 +36,8 @@ public final class Client { } public static void main(String[] args) { - helloButton.addEventListener("click", new EventListener() { - @Override public void handleEvent(Event evt) { + helloButton.addEventListener("click", new EventListener() { + @Override public void handleEvent(MouseEvent evt) { sayHello(); } }); diff --git a/teavm-samples/teavm-samples-video/src/main/java/org/teavm/samples/video/Player.java b/teavm-samples/teavm-samples-video/src/main/java/org/teavm/samples/video/Player.java index f6e3a45cd..dd7c37fe2 100644 --- a/teavm-samples/teavm-samples-video/src/main/java/org/teavm/samples/video/Player.java +++ b/teavm-samples/teavm-samples-video/src/main/java/org/teavm/samples/video/Player.java @@ -16,8 +16,8 @@ package org.teavm.samples.video; import org.teavm.dom.browser.Window; -import org.teavm.dom.events.Event; import org.teavm.dom.events.EventListener; +import org.teavm.dom.events.MouseEvent; import org.teavm.dom.html.HTMLBodyElement; import org.teavm.dom.html.HTMLButtonElement; import org.teavm.dom.html.HTMLDocument; @@ -69,126 +69,126 @@ public final class Player { HTMLButtonElement loadButton = (HTMLButtonElement)document.createElement("button"); loadButton.appendChild(document.createTextNode("load()")); - loadButton.addEventListener("click", new EventListener() { + loadButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.load(); } }); HTMLButtonElement playButton = (HTMLButtonElement)document.createElement("button"); playButton.appendChild(document.createTextNode("play()")); - playButton.addEventListener("click", new EventListener() { + playButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.play(); } }); HTMLButtonElement pauseButton = (HTMLButtonElement)document.createElement("button"); pauseButton.appendChild(document.createTextNode("pause()")); - pauseButton.addEventListener("click", new EventListener() { + pauseButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.pause(); } }); HTMLButtonElement currentTimePlusButton = (HTMLButtonElement)document.createElement("button"); currentTimePlusButton.appendChild(document.createTextNode("currentTime+=10")); - currentTimePlusButton.addEventListener("click", new EventListener() { + currentTimePlusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setCurrentTime(video.getCurrentTime() + 10); } }); HTMLButtonElement currentTimeMinusButton = (HTMLButtonElement)document.createElement("button"); currentTimeMinusButton.appendChild(document.createTextNode("currentTime-=10")); - currentTimeMinusButton.addEventListener("click", new EventListener() { + currentTimeMinusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setCurrentTime(video.getCurrentTime() - 10); } }); HTMLButtonElement currentTime50Button = (HTMLButtonElement)document.createElement("button"); currentTime50Button.appendChild(document.createTextNode("currentTime=50")); - currentTime50Button.addEventListener("click", new EventListener() { + currentTime50Button.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setCurrentTime(50); } }); HTMLButtonElement playbackRateIncrementButton = (HTMLButtonElement)document.createElement("button"); playbackRateIncrementButton.appendChild(document.createTextNode("playbackRate++")); - playbackRateIncrementButton.addEventListener("click", new EventListener() { + playbackRateIncrementButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setPlaybackRate(video.getPlaybackRate() + 1); } }); HTMLButtonElement playbackRateDecrementButton = (HTMLButtonElement)document.createElement("button"); playbackRateDecrementButton.appendChild(document.createTextNode("playbackRate--")); - playbackRateDecrementButton.addEventListener("click", new EventListener() { + playbackRateDecrementButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setPlaybackRate(video.getPlaybackRate() - 1); } }); HTMLButtonElement playbackRatePlusButton = (HTMLButtonElement)document.createElement("button"); playbackRatePlusButton.appendChild(document.createTextNode("playbackRate+=0.1")); - playbackRatePlusButton.addEventListener("click", new EventListener() { + playbackRatePlusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setPlaybackRate(video.getPlaybackRate() + 0.1); } }); HTMLButtonElement playbackRateMinusButton = (HTMLButtonElement)document.createElement("button"); playbackRateMinusButton.appendChild(document.createTextNode("playbackRate-=0.1")); - playbackRateMinusButton.addEventListener("click", new EventListener() { + playbackRateMinusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setPlaybackRate(video.getPlaybackRate() - 0.1); } }); HTMLButtonElement volumePlusButton = (HTMLButtonElement)document.createElement("button"); volumePlusButton.appendChild(document.createTextNode("volume+=0.1")); - volumePlusButton.addEventListener("click", new EventListener() { + volumePlusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setVolume(video.getVolume() + 0.1f); } }); HTMLButtonElement volumeMinusButton = (HTMLButtonElement)document.createElement("button"); volumeMinusButton.appendChild(document.createTextNode("volume-=0.1")); - volumeMinusButton.addEventListener("click", new EventListener() { + volumeMinusButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setVolume(video.getVolume() - 0.1f); } }); HTMLButtonElement muteButton = (HTMLButtonElement)document.createElement("button"); muteButton.appendChild(document.createTextNode("muted=true")); - muteButton.addEventListener("click", new EventListener() { + muteButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setMuted(true); } }); HTMLButtonElement unmuteButton = (HTMLButtonElement)document.createElement("button"); unmuteButton.appendChild(document.createTextNode("muted=false")); - unmuteButton.addEventListener("click", new EventListener() { + unmuteButton.addEventListener("click", new EventListener() { @Override - public void handleEvent(Event evt) { + public void handleEvent(MouseEvent evt) { video.setMuted(false); } });