From f5d9ff5ab67fecf68a1028250f70f1fff06eb92c Mon Sep 17 00:00:00 2001 From: Mirko Sertic Date: Mon, 17 Dec 2018 14:48:31 +0100 Subject: [PATCH] Fixed #80 Allow more generic upcall from Host to Bytecoder for OpaqueReferenceTypes --- .../mirkosertic/bytecoder/api/Callback.java | 3 +- .../api/web/AnimationFrameCallback.java} | 11 +- .../bytecoder/api/web/EventListener.java | 23 ++ .../bytecoder/api/web/EventTarget.java | 3 +- .../mirkosertic/bytecoder/api/web/Window.java | 3 +- .../bytecoder/backend/CompileTarget.java | 25 +- .../bytecoder/backend/js/JSSSAWriter.java | 20 +- .../wasm/WASMSSAASTCompilerBackend.java | 243 ++++++++++++++---- .../bytecoder/core/OpaqueReferenceTest.java | 4 +- .../core/OpaqueReferenceKotlinTest.kt | 6 +- .../integrationtest/JBox2DSimulation.java | 20 +- .../integrationtest/JBox2DSimulationKotlin.kt | 7 +- manual/README.md | 21 +- 13 files changed, 292 insertions(+), 97 deletions(-) rename classlib/{java.base/src/main/java/de/mirkosertic/bytecoder/classlib/Globals.java => bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/AnimationFrameCallback.java} (65%) create mode 100644 classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventListener.java diff --git a/classlib/bytecoder.api/src/main/java/de/mirkosertic/bytecoder/api/Callback.java b/classlib/bytecoder.api/src/main/java/de/mirkosertic/bytecoder/api/Callback.java index c80c5a7c0d..ed713936e5 100644 --- a/classlib/bytecoder.api/src/main/java/de/mirkosertic/bytecoder/api/Callback.java +++ b/classlib/bytecoder.api/src/main/java/de/mirkosertic/bytecoder/api/Callback.java @@ -15,7 +15,6 @@ */ package de.mirkosertic.bytecoder.api; -public interface Callback { +public interface Callback { - void run(T aValue); } diff --git a/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/Globals.java b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/AnimationFrameCallback.java similarity index 65% rename from classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/Globals.java rename to classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/AnimationFrameCallback.java index 86cb302031..cb77dec005 100644 --- a/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/Globals.java +++ b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/AnimationFrameCallback.java @@ -13,16 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.mirkosertic.bytecoder.classlib; +package de.mirkosertic.bytecoder.api.web; import de.mirkosertic.bytecoder.api.Callback; -import de.mirkosertic.bytecoder.api.Export; -import de.mirkosertic.bytecoder.api.OpaqueReferenceType; -public class Globals { +public interface AnimationFrameCallback extends Callback { - @Export("summonCallback") - public static void summonCallback(final Callback aCallback, final OpaqueReferenceType aArgument) { - aCallback.run(aArgument); - } + void run(int aElapsedTime); } diff --git a/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventListener.java b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventListener.java new file mode 100644 index 0000000000..d7a6a9502c --- /dev/null +++ b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventListener.java @@ -0,0 +1,23 @@ +/* + * Copyright 2018 Mirko Sertic + * + * 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 de.mirkosertic.bytecoder.api.web; + +import de.mirkosertic.bytecoder.api.Callback; + +public interface EventListener extends Callback { + + void run(T aEvent); +} diff --git a/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventTarget.java b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventTarget.java index 2a022d31e0..457c02a4b7 100644 --- a/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventTarget.java +++ b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/EventTarget.java @@ -15,12 +15,11 @@ */ package de.mirkosertic.bytecoder.api.web; -import de.mirkosertic.bytecoder.api.Callback; import de.mirkosertic.bytecoder.api.OpaqueReferenceType; public interface EventTarget extends OpaqueReferenceType { - void addEventListener(final String eventType, final Callback aEventListener); + void addEventListener(final String eventType, final EventListener aEventListener); void dispatchEvent(T aEvent); } diff --git a/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/Window.java b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/Window.java index f92b744d3d..4559a06c3b 100644 --- a/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/Window.java +++ b/classlib/bytecoder.web/src/main/java/de/mirkosertic/bytecoder/api/web/Window.java @@ -15,7 +15,6 @@ */ package de.mirkosertic.bytecoder.api.web; -import de.mirkosertic.bytecoder.api.Callback; import de.mirkosertic.bytecoder.api.Import; import de.mirkosertic.bytecoder.api.OpaqueProperty; @@ -27,5 +26,5 @@ public abstract class Window implements EventTarget { @OpaqueProperty("document") public abstract HTMLDocument document(); - public abstract void requestAnimationFrame(Callback callback); + public abstract void requestAnimationFrame(AnimationFrameCallback aCallback); } diff --git a/core/src/main/java/de/mirkosertic/bytecoder/backend/CompileTarget.java b/core/src/main/java/de/mirkosertic/bytecoder/backend/CompileTarget.java index f8a779b2cd..5b8807d903 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/backend/CompileTarget.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/backend/CompileTarget.java @@ -15,14 +15,12 @@ */ package de.mirkosertic.bytecoder.backend; -import java.lang.reflect.Method; - import de.mirkosertic.bytecoder.backend.js.JSSSACompilerBackend; import de.mirkosertic.bytecoder.backend.js.JSWriterUtils; import de.mirkosertic.bytecoder.backend.wasm.WASMSSAASTCompilerBackend; -import de.mirkosertic.bytecoder.classlib.Globals; import de.mirkosertic.bytecoder.classlib.VM; import de.mirkosertic.bytecoder.core.BytecodeArrayTypeRef; +import de.mirkosertic.bytecoder.core.BytecodeClass; import de.mirkosertic.bytecoder.core.BytecodeLinkedClass; import de.mirkosertic.bytecoder.core.BytecodeLinkerContext; import de.mirkosertic.bytecoder.core.BytecodeLoader; @@ -33,6 +31,10 @@ import de.mirkosertic.bytecoder.core.BytecodeTypeRef; import de.mirkosertic.bytecoder.ssa.NaiveProgramGenerator; +import java.lang.reflect.Method; +import java.util.List; +import java.util.stream.Collectors; + public class CompileTarget { public enum BackendType { @@ -68,8 +70,6 @@ public CompileResult compile( theClassLinkedCass.resolveConstructorInvocation(new BytecodeMethodSignature( BytecodePrimitiveTypeRef.VOID, new BytecodeTypeRef[] {})); - theLinkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(Globals.class)); - // Lambda handling final BytecodeLinkedClass theCallsite = theLinkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(VM.ImplementingCallsite.class)); theCallsite.resolveVirtualMethod("invokeExact", new BytecodeMethodSignature(BytecodeObjectTypeRef.fromRuntimeClass(Object.class), @@ -85,6 +85,21 @@ public CompileResult compile( theClass.resolveVirtualMethod(aMethodName, aSignature); } + // We have to link all callback implementations. They are not part of the dependency yet as + // they are not invoked by the bytecode, but from the outside world. By adding them to the + // dependency tree, we make sure they are available for invocation. + List theLinkedClasses = theLinkerContext.linkedClasses().map(t -> t.targetNode()).collect(Collectors.toList()); + for (BytecodeLinkedClass theLinkedClass : theLinkedClasses) { + if (theLinkedClass.isCallback()) { + BytecodeClass theBytecodeClass = theLinkedClass.getBytecodeClass(); + for (BytecodeMethod theCallbackMethod : theBytecodeClass.getMethods()) { + if (!theCallbackMethod.isConstructor() && !theCallbackMethod.isClassInitializer()) { + theLinkedClass.resolveVirtualMethod(theCallbackMethod.getName().stringValue(), theCallbackMethod.getSignature()); + } + } + } + } + // Before code generation we have to make sure that all abstract method implementations are linked correctly aOptions.getLogger().info("Resolving abstract method hierarchy"); theLinkerContext.resolveAbstractMethodsInSubclasses(); diff --git a/core/src/main/java/de/mirkosertic/bytecoder/backend/js/JSSSAWriter.java b/core/src/main/java/de/mirkosertic/bytecoder/backend/js/JSSSAWriter.java index 266a1410a4..fdad543989 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/backend/js/JSSSAWriter.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/backend/js/JSSSAWriter.java @@ -29,6 +29,7 @@ import de.mirkosertic.bytecoder.core.BytecodeMethodSignature; import de.mirkosertic.bytecoder.core.BytecodeObjectTypeRef; import de.mirkosertic.bytecoder.core.BytecodeOpcodeAddress; +import de.mirkosertic.bytecoder.core.BytecodeResolvedMethods; import de.mirkosertic.bytecoder.core.BytecodeTypeRef; import de.mirkosertic.bytecoder.core.BytecodeUtf8Constant; import de.mirkosertic.bytecoder.core.BytecodeVirtualMethodIdentifier; @@ -641,11 +642,24 @@ private void printToJSConvertedValue(final BytecodeTypeRef aTypeRef, final Value if (theLinkedClass.isOpaqueType()) { print(aValue); } else if (theLinkedClass.isCallback()) { - print("function(event) {"); + + BytecodeResolvedMethods theMethods = theLinkedClass.resolvedMethods(); + List availableCallbacks = theMethods.stream().filter(t -> !t.getValue().isConstructor() && !t.getValue().isClassInitializer() + && !t.getProvidingClass().getClassName().name().equals(Object.class.getName())).map(t -> t.getValue()).collect(Collectors.toList()); + if (availableCallbacks.size() != 1) { + throw new IllegalStateException("Unvalid number of callback methods available for type " + theLinkedClass.getClassName().name() + ", expected 1, got " + availableCallbacks.size()); + } + + BytecodeMethod theCallbackMethod = availableCallbacks.get(0); + String theMethodName = JSWriterUtils.toMethodName(theCallbackMethod.getName().stringValue(), theCallbackMethod.getSignature()); + + print("function() {"); print("var v = "); print(aValue); - print(";v"); - print(".VOIDrundmbaOpaqueReferenceType(v, event);"); + print(";var args = Array.prototype.slice.call(arguments);args.unshift(v);v"); + print("."); + print(theMethodName); + print(".apply(v, args);"); print("}"); } else { throw new IllegalStateException("Type conversion to " + aTypeRef.name() + " is not supported!"); diff --git a/core/src/main/java/de/mirkosertic/bytecoder/backend/wasm/WASMSSAASTCompilerBackend.java b/core/src/main/java/de/mirkosertic/bytecoder/backend/wasm/WASMSSAASTCompilerBackend.java index d4fa52c84b..b83526d346 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/backend/wasm/WASMSSAASTCompilerBackend.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/backend/wasm/WASMSSAASTCompilerBackend.java @@ -24,29 +24,10 @@ import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionReference; import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionTableReference; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.invoke.LambdaMetafactory; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import de.mirkosertic.bytecoder.api.Callback; import de.mirkosertic.bytecoder.api.EmulatedByRuntime; import de.mirkosertic.bytecoder.api.Export; import de.mirkosertic.bytecoder.api.OpaqueMethod; import de.mirkosertic.bytecoder.api.OpaqueProperty; -import de.mirkosertic.bytecoder.api.OpaqueReferenceType; import de.mirkosertic.bytecoder.backend.CompileBackend; import de.mirkosertic.bytecoder.backend.CompileOptions; import de.mirkosertic.bytecoder.backend.ConstantPool; @@ -97,6 +78,23 @@ import de.mirkosertic.bytecoder.ssa.Value; import de.mirkosertic.bytecoder.ssa.Variable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.invoke.LambdaMetafactory; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + public class WASMSSAASTCompilerBackend implements CompileBackend { private static class CallSite { @@ -161,10 +159,6 @@ public WASMCompileResult generateCodeFor( String.class), new BytecodeTypeRef[] {new BytecodeArrayTypeRef(BytecodePrimitiveTypeRef.BYTE, 1)})); theMemoryManagerClass.resolveStaticMethod("newByteArray", new BytecodeMethodSignature(new BytecodeArrayTypeRef(BytecodePrimitiveTypeRef.BYTE, 1), new BytecodeTypeRef[] {BytecodePrimitiveTypeRef.INT})); theMemoryManagerClass.resolveStaticMethod("setByteArrayEntry", new BytecodeMethodSignature(BytecodePrimitiveTypeRef.VOID, new BytecodeTypeRef[] {new BytecodeArrayTypeRef(BytecodePrimitiveTypeRef.BYTE, 1), BytecodePrimitiveTypeRef.INT, BytecodePrimitiveTypeRef.BYTE})); - theMemoryManagerClass.resolveStaticMethod("summonCallback", - new BytecodeMethodSignature(BytecodePrimitiveTypeRef.VOID, new BytecodeTypeRef[] {BytecodeObjectTypeRef.fromRuntimeClass( - Callback.class), BytecodeObjectTypeRef.fromRuntimeClass( - OpaqueReferenceType.class)})); final BytecodeMethodSignature pushExceptionSignature = new BytecodeMethodSignature(BytecodePrimitiveTypeRef.VOID, new BytecodeTypeRef[] {BytecodeObjectTypeRef.fromRuntimeClass(Throwable.class)}); final BytecodeMethodSignature popExceptionSignature = new BytecodeMethodSignature(BytecodeObjectTypeRef.fromRuntimeClass(Throwable.class), new BytecodeTypeRef[0]); @@ -926,6 +920,62 @@ public Function resolveCallsiteBootstrapFor(final BytecodeClass owningClass, fin mainFunction.exportAs("main"); } + // We need to generate + aLinkerContext.linkedClasses().map(t -> t.targetNode()).filter(t -> t.isCallback() && t.getBytecodeClass().getAccessFlags().isInterface()).forEach(t -> { + + BytecodeResolvedMethods theMethods = t.resolvedMethods(); + List availableCallbacks = theMethods.stream().filter(x -> !x.getValue().isConstructor() && !x.getValue().isClassInitializer() + && x.getProvidingClass() == t).map(x -> x.getValue()).collect(Collectors.toList()); + + if (availableCallbacks.size() > 0) { + + if (availableCallbacks.size() != 1) { + throw new IllegalStateException( + "Unvalid number of callback methods available for type " + t.getClassName().name() + + ", expected 1, got " + availableCallbacks.size()); + } + + BytecodeMethod theDelegateMethod = availableCallbacks.get(0); + + // Now we need to create a delegate method for this + final List theSignatureParams = new ArrayList<>(); + + List theArguments = new ArrayList<>(); + theSignatureParams.add(PrimitiveType.i32); + theArguments.add(param("target", PrimitiveType.i32)); + BytecodeTypeRef[] theSignatureArguments = theDelegateMethod.getSignature().getArguments(); + for (int i = 0; i < theSignatureArguments.length; i++) { + PrimitiveType theSignatureType = WASMSSAASTWriter.toType(TypeRef.toType(theSignatureArguments[i])); + theArguments.add(param("param" + i, theSignatureType)); + theSignatureParams.add(theSignatureType); + } + + WASMType theCalledFunction = module.getTypes().typeFor(theSignatureParams); + + BytecodeVirtualMethodIdentifier theMethodIdentifier = aLinkerContext.getMethodCollection().identifierFor(theDelegateMethod); + + String theFunctionName = WASMWriterUtils + .toMethodName(t.getClassName(), theDelegateMethod.getName(), theDelegateMethod.getSignature()); + ExportableFunction theFunction = module.getFunctions().newFunction(theFunctionName, theArguments); + + final WASMType theResolveType = module.getTypes().typeFor(Arrays.asList(PrimitiveType.i32, PrimitiveType.i32), PrimitiveType.i32); + final List theResolveArgument = new ArrayList<>(); + theResolveArgument.add(getLocal(theFunction.localByLabel("target"))); + theResolveArgument.add(i32.c(theMethodIdentifier.getIdentifier())); + final WASMValue theIndex = call(theResolveType, theResolveArgument, i32.load(4, getLocal(theFunction.localByLabel("target")))); + + List theOtherArguments = new ArrayList<>(); + theOtherArguments.add(getLocal(theFunction.localByLabel("target"))); + for (int i = 0; i < theSignatureArguments.length; i++) { + theOtherArguments.add(getLocal(theFunction.localByLabel("param" + i))); + } + + theFunction.flow.voidCallIndirect(theCalledFunction, theOtherArguments, theIndex); + + theFunction.exportAs(theFunctionName); + } + }); + final StringWriter theStringWriter = new StringWriter(); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { @@ -990,21 +1040,6 @@ public Function resolveCallsiteBootstrapFor(final BytecodeClass owningClass, fin theWriter.println(" },"); theWriter.println(); - - theWriter.println(); - theWriter.println(" toJSEventListener: function(value) {"); - theWriter.println(" return function(event) {"); - theWriter.println(" try {"); - theWriter.println(" var eventIndex = bytecoder.toBytecoderReference(event);"); - theWriter.println(" bytecoder.exports.summonCallback(0,value,eventIndex);"); - theWriter.println(" delete bytecoder.referenceTable[eventIndex];"); - theWriter.println(" } catch (e) {"); - theWriter.println(" console.log(e);"); - theWriter.println(" }"); - theWriter.println(" };"); - theWriter.println(" },"); - theWriter.println(); - theWriter.println(" toBytecoderReference: function(value) {"); theWriter.println(" var index = bytecoder.referenceTable.indexOf(value);"); theWriter.println(" if (index>=0) {"); @@ -1145,7 +1180,7 @@ public Function resolveCallsiteBootstrapFor(final BytecodeClass owningClass, fin boolean theWriteClosingBraces = false; - final String theConversionFunction = conversionFunctionToWASNForOpaqueType(aLinkerContext, theSignature.getReturnType()); + final String theConversionFunction = conversionFunctionToWASMForOpaqueType(aLinkerContext, theSignature.getReturnType()); if (theConversionFunction != null) { theWriter.print(theConversionFunction); theWriter.print("("); @@ -1173,7 +1208,7 @@ public Function resolveCallsiteBootstrapFor(final BytecodeClass owningClass, fin if (!theSignature.getReturnType().isVoid()) { theWriter.print(" return "); - final String theConversionFunction = conversionFunctionToWASNForOpaqueType(aLinkerContext, theSignature.getReturnType()); + final String theConversionFunction = conversionFunctionToWASMForOpaqueType(aLinkerContext, theSignature.getReturnType()); if (theConversionFunction != null) { theWriter.print(theConversionFunction); theWriter.print("("); @@ -1193,16 +1228,128 @@ public Function resolveCallsiteBootstrapFor(final BytecodeClass owningClass, fin theWriter.print(","); } - final String theConversionFunction = conversionFunctionToJSForOpaqueType(aLinkerContext, theSignature.getArguments()[i]); - if (theConversionFunction != null) { - theWriter.print(theConversionFunction); - theWriter.print("(arg"); + BytecodeTypeRef theRef = theSignature.getArguments()[i]; + if (theRef.isPrimitive()) { + theWriter.print("arg"); theWriter.print(i); - theWriter.print(")"); - } else { + } else if (theRef.matchesExactlyTo(BytecodeObjectTypeRef.fromRuntimeClass(String.class))) { + theWriter.print("bytecoder.toJSString("); theWriter.print("arg"); theWriter.print(i); + theWriter.print(")"); + } else { + final BytecodeObjectTypeRef theObjectType = (BytecodeObjectTypeRef) theRef; + final BytecodeLinkedClass theLinkedClass = aLinkerContext.resolveClass(theObjectType); + if (theLinkedClass.isOpaqueType()) { + theWriter.print("bytecoder.toJSReference("); + theWriter.print("arg"); + theWriter.print(i); + theWriter.print(")"); + } else if (theLinkedClass.isCallback()) { + + List theCallbackMethods = theLinkedClass.resolvedMethods().stream().filter(x -> x.getProvidingClass() == theLinkedClass).map(x -> x.getValue()).collect(Collectors.toList()); + if (theCallbackMethods.size() != 1) { + throw new IllegalStateException("Wrong number of callback methods in " + theLinkedClass.getClassName().name() + ", expected 1, got " + theCallbackMethods.size()); + } + BytecodeMethod theImpl = theCallbackMethods.get(0); + + theWriter.print("function ("); + for (int j=0;j0) { + theWriter.print(","); + } + theWriter.print("farg"); + theWriter.print(j); + } + theWriter.print(") {"); + + for (int j=0;j0) { + theWriter.print(","); + } + theWriter.print("var marg"); + theWriter.print(j); + theWriter.print("="); + + BytecodeTypeRef theTypeRef = theImpl.getSignature().getArguments()[j]; + + if (theTypeRef.isPrimitive()) { + theWriter.print("farg"); + theWriter.print(j); + } else if (theTypeRef.isArray()) { + throw new IllegalStateException("Type conversion to " + theTypeRef.name() + " is not supported!"); + } else if (theTypeRef.matchesExactlyTo(BytecodeObjectTypeRef.fromRuntimeClass(String.class))) { + theWriter.print("bytecoder.toBytecoderString("); + theWriter.print("farg"); + theWriter.print(j); + theWriter.print(")"); + } else { + final BytecodeObjectTypeRef theArgObjectType = (BytecodeObjectTypeRef) theTypeRef; + final BytecodeLinkedClass theArgLinkedClass = aLinkerContext.resolveClass(theArgObjectType); + if (!theArgLinkedClass.isOpaqueType()) { + throw new IllegalStateException("Type conversion from " + theTypeRef.name() + " is not supported!"); + + } + theWriter.print("bytecoder.toBytecoderReference("); + theWriter.print("farg"); + theWriter.print(j); + theWriter.print(")"); + } + theWriter.print(";"); + } + + String theCallbackMethod = WASMWriterUtils.toMethodName(theLinkedClass.getClassName(), theImpl.getName(), theImpl.getSignature()); + theWriter.print("bytecoder.exports."); + theWriter.print(theCallbackMethod); + theWriter.print("(arg"); + theWriter.print(i); + + for (int j=0;j() { + w.document().addEventListener("click", new EventListener() { @Override public void run(final Event aValue) { w.document().title("clicked!"); diff --git a/core/src/test/kotlin/de/mirkosertic/bytecoder/core/OpaqueReferenceKotlinTest.kt b/core/src/test/kotlin/de/mirkosertic/bytecoder/core/OpaqueReferenceKotlinTest.kt index 70d79d57de..63ece63a22 100644 --- a/core/src/test/kotlin/de/mirkosertic/bytecoder/core/OpaqueReferenceKotlinTest.kt +++ b/core/src/test/kotlin/de/mirkosertic/bytecoder/core/OpaqueReferenceKotlinTest.kt @@ -15,8 +15,8 @@ */ package de.mirkosertic.bytecoder.core -import de.mirkosertic.bytecoder.api.Callback import de.mirkosertic.bytecoder.api.web.ClickEvent +import de.mirkosertic.bytecoder.api.web.EventListener import de.mirkosertic.bytecoder.api.web.HTMLDocument import de.mirkosertic.bytecoder.api.web.Window import de.mirkosertic.bytecoder.unittest.BytecoderUnitTestRunner @@ -35,7 +35,7 @@ class OpaqueReferenceKotlinTest { fun setTitleTest() { val w = Window.window() val d = w.document() - d.addEventListener("click", Callback { + d.addEventListener("click", EventListener { d.title("Hello world!!") }) } @@ -44,7 +44,7 @@ class OpaqueReferenceKotlinTest { fun setTitleTestMember() { window = Window.window() document = window!!.document() - document!!.addEventListener("click", Callback { + document!!.addEventListener("click", EventListener { document!!.title("Hello world!!") }) } diff --git a/integrationtest/src/main/java/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulation.java b/integrationtest/src/main/java/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulation.java index bb3fe7ced3..46d89a741b 100644 --- a/integrationtest/src/main/java/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulation.java +++ b/integrationtest/src/main/java/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulation.java @@ -15,13 +15,13 @@ */ package de.mirkosertic.bytecoder.integrationtest; -import de.mirkosertic.bytecoder.api.Callback; import de.mirkosertic.bytecoder.api.Export; import de.mirkosertic.bytecoder.api.Import; +import de.mirkosertic.bytecoder.api.web.AnimationFrameCallback; import de.mirkosertic.bytecoder.api.web.CanvasRenderingContext2D; import de.mirkosertic.bytecoder.api.web.ClickEvent; import de.mirkosertic.bytecoder.api.web.Document; -import de.mirkosertic.bytecoder.api.web.Event; +import de.mirkosertic.bytecoder.api.web.EventListener; import de.mirkosertic.bytecoder.api.web.HTMLCanvasElement; import de.mirkosertic.bytecoder.api.web.HTMLElement; import de.mirkosertic.bytecoder.api.web.Window; @@ -171,21 +171,21 @@ public World getWorld() { private static Scene scene; private static CanvasRenderingContext2D renderingContext2D; - private static Callback animationCallback; + private static AnimationFrameCallback animationCallback; private static Window window; @Export("main") public static void main(final String[] args) { scene = new Scene(); window = Window.window(); - Document document = window.document(); + final Document document = window.document(); final HTMLCanvasElement theCanvas = document.getElementById("benchmark-canvas"); renderingContext2D = theCanvas.getContext("2d"); - animationCallback = new Callback() { + animationCallback = new AnimationFrameCallback() { @Override - public void run(Event aValue) { - long theStart = System.currentTimeMillis(); + public void run(final int aElapsedTime) { + final long theStart = System.currentTimeMillis(); statsBegin(); @@ -195,7 +195,7 @@ public void run(Event aValue) { statsEnd(); - int theDuration = (int) (System.currentTimeMillis() - theStart); + final int theDuration = (int) (System.currentTimeMillis() - theStart); logRuntime(theDuration); window.requestAnimationFrame(animationCallback); @@ -203,9 +203,9 @@ public void run(Event aValue) { }; final HTMLElement button = document.getElementById("button"); - button.addEventListener("click", new Callback() { + button.addEventListener("click", new EventListener() { @Override - public void run(ClickEvent aValue) { + public void run(final ClickEvent aValue) { button.style().setProperty("disabled", "true"); window.requestAnimationFrame(animationCallback); } diff --git a/integrationtest/src/main/kotlin/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulationKotlin.kt b/integrationtest/src/main/kotlin/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulationKotlin.kt index 6efa579a73..7d9495cfd4 100644 --- a/integrationtest/src/main/kotlin/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulationKotlin.kt +++ b/integrationtest/src/main/kotlin/de/mirkosertic/bytecoder/integrationtest/JBox2DSimulationKotlin.kt @@ -15,7 +15,6 @@ */ package de.mirkosertic.bytecoder.integrationtest -import de.mirkosertic.bytecoder.api.Callback import de.mirkosertic.bytecoder.api.Export import de.mirkosertic.bytecoder.api.Import import de.mirkosertic.bytecoder.api.web.* @@ -30,7 +29,7 @@ object JBox2DSimulationKotlin { private var scene: Scene? = null private var renderingContext2D: CanvasRenderingContext2D? = null - private var animationCallback: Callback? = null + private var animationCallback: AnimationFrameCallback? = null private var window: Window? = null class Scene { @@ -166,7 +165,7 @@ object JBox2DSimulationKotlin { val theCanvas = document.getElementById("benchmark-canvas") renderingContext2D = theCanvas.getContext("2d") - animationCallback = Callback { + animationCallback = AnimationFrameCallback { val theStart = System.currentTimeMillis() statsBegin() @@ -184,7 +183,7 @@ object JBox2DSimulationKotlin { } val button = document.getElementById("button") - button.addEventListener("click", Callback { + button.addEventListener("click", EventListener { button.style().setProperty("disabled", "true") window!!.requestAnimationFrame(animationCallback) }) diff --git a/manual/README.md b/manual/README.md index dfffa038bf..61202d67ae 100644 --- a/manual/README.md +++ b/manual/README.md @@ -43,7 +43,7 @@ public class OpaqueReferenceTest { } ``` -I'll try to exlain the basics behind this and how it can be compiled to JavaScript or WebAssembly in the following sections. +I'll try to explain the basics behind this and how it can be compiled to JavaScript or WebAssembly in the following sections. ### Interoperability and runtime linkage @@ -75,7 +75,7 @@ Bytecoder also supports event listeners, as seen in the following example: ``` final HTMLElement button = document.getElementById("button"); -button.addEventListener("click", new Callback() { +button.addEventListener("click", new EventListener() { @Override public void run(ClickEvent aValue) { button.style().setProperty("disabled", "true"); @@ -83,6 +83,13 @@ button.addEventListener("click", new Callback() { }); ``` +The OpaqueReferenceType API allows the following types for Bytecoder-Host communication: + +* Primitives(int,float,double,long,char,byte,short) +* `java.lang.String` +* `de.mirkosertic.bytecoder.api.OpaqueReferenceType` and sub classes of it +* `de.mirkosertic.bytecoder.api.Callback` and sub classes of it + #### Importing functionality from the host environment Using host environment functionality is quite common. This can be either simply @@ -192,12 +199,12 @@ PlatformFactory theFactory = new PlatformFactory(); Platform thePlatform = theFactory.createPlatform(); ``` -The `Platform` instance must only be ontained once and can be cached. +The `Platform` instance must only be obtained once and can be cached. If you have multiple GPUs or a system with NVIDIA Optimus technology, you have multiple OpenCL platform available. One for the NVIDIA GPU, and another for the embedded Intel GPU on your CPU. By default Bytecoder logs the available -platforms at startup and selects the OpenCL platform with the hightest number. +platforms at startup and selects the OpenCL platform with the highest number. If this does not fit, you can always add a `OPENCL_PLATFORM=x` to your JVM properties to set the OpenCL platform to number x. @@ -208,8 +215,8 @@ of context instances forces the OpenCL runtime to recompile Kernels, which will cause a huge performance impact. `Context` instances are auto-closable, so they can be used with Java try-with-resource blocks. -OpenCL `Kernel` instances are whe workhorse of the system. They can be created -by subclassing `de.mirkosertic.bytecoder.api.opencl.Kernel` and adding a single +OpenCL `Kernel` instances are the workhorse of the system. They can be created +by sub classing `de.mirkosertic.bytecoder.api.opencl.Kernel` and adding a single method to it. Here is a simple example of the whole workflow: ``` @@ -364,7 +371,7 @@ public void testSimpleLoop() { } ``` -the following intermediate representation graph is generated (in its first, unoptimized form): +the following intermediate representation graph is generated (in its first, un optimized form): ![Intermediate representation graph](docassets/ir_loopexample.svg)