diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 9a1313201..b4b0fbb47 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -15,8 +15,11 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.interop.Address; import org.teavm.interop.Async; +import org.teavm.interop.DelegateTo; import org.teavm.interop.Rename; +import org.teavm.interop.Structure; import org.teavm.interop.Superclass; import org.teavm.interop.Sync; import org.teavm.jso.browser.TimerHandler; @@ -25,6 +28,11 @@ import org.teavm.platform.PlatformObject; import org.teavm.platform.PlatformQueue; import org.teavm.platform.PlatformRunnable; import org.teavm.platform.async.AsyncCallback; +import org.teavm.runtime.Allocator; +import org.teavm.runtime.RuntimeArray; +import org.teavm.runtime.RuntimeClass; +import org.teavm.runtime.RuntimeJavaObject; +import org.teavm.runtime.RuntimeObject; /** * @@ -191,6 +199,7 @@ public class TObject { return getClass().getName() + "@" + TInteger.toHexString(identity()); } + @DelegateTo("identityLowLevel") int identity() { PlatformObject platformThis = Platform.getPlatformObject(this); if (platformThis.getId() == 0) { @@ -199,7 +208,24 @@ public class TObject { return Platform.getPlatformObject(this).getId(); } + @SuppressWarnings("unused") + private static int identityLowLevel(RuntimeJavaObject object) { + if ((object.classReference & RuntimeObject.MONITOR_EXISTS) != 0) { + object = (RuntimeJavaObject) object.monitor; + } + int result = object.monitor.toAddress().toInt(); + if (result == 0) { + result = RuntimeJavaObject.nextId++; + if (result == 0) { + result = RuntimeJavaObject.nextId++; + } + object.monitor = Address.fromInt(result).toStructure(); + } + return result; + } + @Override + @DelegateTo("cloneLowLevel") protected Object clone() throws TCloneNotSupportedException { if (!(this instanceof TCloneable) && Platform.getPlatformObject(this) .getPlatformClass().getMetadata().getArrayItem() == null) { @@ -210,6 +236,28 @@ public class TObject { return result; } + @SuppressWarnings("unused") + private static RuntimeJavaObject cloneLowLevel(RuntimeJavaObject self) { + RuntimeClass cls = RuntimeClass.getClass(self); + int skip = Structure.sizeOf(RuntimeJavaObject.class); + int size; + RuntimeJavaObject copy; + if (cls.itemType == null) { + copy = Allocator.allocate(cls).toStructure(); + size = cls.size; + } else { + RuntimeArray array = (RuntimeArray) self; + copy = Allocator.allocateArray(cls, array.size).toStructure(); + int itemSize = (cls.itemType.flags & RuntimeClass.PRIMITIVE) == 0 ? 4 : cls.itemType.size; + Address headerSize = Address.align(Address.fromInt(Structure.sizeOf(RuntimeArray.class)), itemSize); + size = itemSize * array.size + headerSize.toInt(); + } + if (size > skip) { + Allocator.moveMemoryBlock(self.toAddress().add(skip), copy.toAddress().add(skip), size - skip); + } + return copy; + } + @Sync @Rename("notify") public final void notify0() { diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 2cf6e3039..5583d9d64 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -33,6 +33,7 @@ public final class Example { testHashCode(); testArrayList(); testArrayCopy(); + testArrayIsObject(); } private static void testFibonacci() { @@ -121,6 +122,15 @@ public final class Example { println(sb.toString()); } + private static void testArrayIsObject() { + byte[] array = new byte[] { 1, 2, 3 }; + byte[] copy = array.clone(); + println("array.hashCode() = " + array.hashCode()); + println("copy.hashCode() = " + copy.hashCode()); + println("array.equals(array) = " + array.equals(array)); + println("array.equals(copy) = " + array.equals(copy)); + } + private static Base instance(int index) { switch (index) { case 0: diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 76679829c..5c67eafd1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -31,6 +31,7 @@ import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmGenerationContext; import org.teavm.backend.wasm.generate.WasmGenerator; +import org.teavm.backend.wasm.generate.WasmGeneratorUtil; import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; @@ -43,13 +44,16 @@ import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic; import org.teavm.backend.wasm.intrinsics.StructureIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic; import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; @@ -59,7 +63,6 @@ import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.patches.ClassPatch; -import org.teavm.backend.wasm.patches.ObjectPatch; import org.teavm.backend.wasm.render.WasmCRenderer; import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyChecker; @@ -124,7 +127,6 @@ public class WasmTarget implements TeaVMTarget { @Override public List getTransformers() { List transformers = new ArrayList<>(); - transformers.add(new ObjectPatch()); transformers.add(new ClassPatch()); transformers.add(new WasmDependencyListener()); return transformers; @@ -257,7 +259,11 @@ public class WasmTarget implements TeaVMTarget { if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) { continue; } - module.add(generator.generate(method.getReference(), implementor)); + if (method == implementor) { + module.add(generator.generate(method.getReference(), implementor)); + } else { + module.add(generateStub(method, implementor)); + } if (controller.wasCancelled()) { return; } @@ -328,6 +334,33 @@ public class WasmTarget implements TeaVMTarget { } } + private WasmFunction generateStub(MethodHolder method, MethodHolder implementor) { + WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(method.getReference())); + if (!method.hasModifier(ElementModifier.STATIC)) { + function.getParameters().add(WasmType.INT32); + } + ValueType[] parameterTypes = method.getParameterTypes(); + for (ValueType parameterType : parameterTypes) { + function.getParameters().add(WasmGeneratorUtil.mapType(parameterType)); + } + + WasmCall call = new WasmCall(WasmMangling.mangleMethod(implementor.getReference())); + for (WasmType param : function.getParameters()) { + WasmLocal local = new WasmLocal(param); + function.add(local); + call.getArguments().add(new WasmGetLocal(local)); + } + + function.setResult(WasmGeneratorUtil.mapType(method.getResultType())); + + if (method.getResultType() == ValueType.VOID) { + function.getBody().add(new WasmDrop(call)); + } else { + function.getBody().add(new WasmReturn(call)); + } + return function; + } + private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator, WasmModule module) { for (String className : classes.getClassNames()) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 037422ff8..5d1d9b650 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -123,11 +123,20 @@ public class WasmClassGenerator { addClass(itemType); ClassBinaryData itemBinaryData = binaryDataMap.get(itemType); + VirtualTable vtable = vtableProvider.lookup("java.lang.Object"); + int vtableSize = vtable != null ? vtable.getEntries().size() : 0; + DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize); + DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue(); + + if (vtableSize > 0) { + fillVirtualTable(vtable, wrapper.getValue(1)); + } + binaryData.size = 4; - binaryData.data = classStructure.createValue(); + binaryData.data = wrapper.getValue(0); binaryData.data.setInt(1, 4); binaryData.data.setAddress(5, itemBinaryData.start); - binaryData.start = binaryWriter.append(binaryData.data); + binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data); itemBinaryData.data.setAddress(6, binaryData.start); } @@ -161,10 +170,15 @@ public class WasmClassGenerator { int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); header.setInt(3, tag); header.setInt(4, RuntimeClass.computeCanary(binaryData.size, tag)); - if (vtable == null) { - return header; + + if (vtable != null) { + fillVirtualTable(vtable, array); } + return vtable != null ? wrapper : header; + } + + private void fillVirtualTable(VirtualTable vtable, DataValue array) { int index = 0; for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) { int methodIndex; @@ -180,8 +194,6 @@ public class WasmClassGenerator { array.setInt(index++, methodIndex); } - - return wrapper; } public int getClassPointer(ValueType type) { diff --git a/core/src/main/java/org/teavm/backend/wasm/patches/ClassPatch.java b/core/src/main/java/org/teavm/backend/wasm/patches/ClassPatch.java index 92dfd6595..7b645eb7b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/patches/ClassPatch.java +++ b/core/src/main/java/org/teavm/backend/wasm/patches/ClassPatch.java @@ -37,7 +37,6 @@ public class ClassPatch implements ClassHolderTransformer { if (!cls.getName().equals("java.lang.Class")) { return; } - for (MethodHolder method : cls.getMethods()) { patchProgram(method.getProgram()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/patches/ObjectPatch.java b/core/src/main/java/org/teavm/backend/wasm/patches/ObjectPatch.java deleted file mode 100644 index 89e7ef459..000000000 --- a/core/src/main/java/org/teavm/backend/wasm/patches/ObjectPatch.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016 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.backend.wasm.patches; - -import org.teavm.diagnostics.Diagnostics; -import org.teavm.interop.Address; -import org.teavm.model.ClassHolder; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.MethodDescriptor; -import org.teavm.model.MethodHolder; -import org.teavm.model.Program; -import org.teavm.model.util.ProgramUtils; -import org.teavm.runtime.RuntimeJavaObject; -import org.teavm.runtime.RuntimeObject; - -public class ObjectPatch implements ClassHolderTransformer { - @Override - public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { - if (!cls.getName().equals("java.lang.Object")) { - return; - } - - MethodHolder method = cls.getMethod(new MethodDescriptor("identity", int.class)); - Program patchedProgram = ProgramUtils.copy(innerSource.get(ObjectPatch.class.getName()) - .getMethod(new MethodDescriptor("patchedIdentity", int.class)).getProgram()); - method.setProgram(patchedProgram); - } - - @SuppressWarnings("unused") - private int patchedIdentity() { - RuntimeJavaObject object = Address.ofObject(this).toStructure(); - if ((object.classReference & RuntimeObject.MONITOR_EXISTS) != 0) { - object = (RuntimeJavaObject) object.monitor; - } - int result = object.monitor.toAddress().toInt(); - if (result == 0) { - result = RuntimeJavaObject.nextId++; - if (result == 0) { - result = RuntimeJavaObject.nextId++; - } - object.monitor = Address.fromInt(result).toStructure(); - } - return result; - } -} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 23acbaf37..4ac54e2c2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -121,7 +121,7 @@ public class WasmCRenderer { indent(); for (int i = 0; i < module.getFunctionTable().size() - 1; ++i) { WasmFunction function = module.getFunctionTable().get(i); - line(function.getName() + ","); + line((function != null ? function.getName() : "unknown") + ","); } line(module.getFunctionTable().get(module.getFunctionTable().size() - 1).getName()); outdent(); diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index f3b088eea..5cc00422b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -643,6 +643,11 @@ class DependencyGraphBuilder { arrayNode.addConsumer(receiverNode::propagate); arrayNode.getArrayItem().connect(receiverNode.getArrayItem()); } + MethodDependency cloneDep = dependencyChecker.linkMethod( + new MethodReference(Object.class, "clone", Object.class), + new CallLocation(caller.getMethod(), currentLocation)); + arrayNode.connect(cloneDep.getVariable(0)); + cloneDep.use(); } @Override