From 35b59ed916b94915cc678495b3f1704156f0dfaf Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 19 Aug 2016 12:36:09 +0300 Subject: [PATCH] Add support of Object.hashCode --- .../javascript/backend/JavaScriptTarget.java | 6 ++ .../java/org/teavm/runtime/RuntimeArray.java | 3 +- .../org/teavm/runtime/RuntimeJavaObject.java | 21 +++++++ .../java/org/teavm/runtime/RuntimeObject.java | 3 + core/src/main/java/org/teavm/vm/TeaVM.java | 4 ++ .../main/java/org/teavm/vm/TeaVMTarget.java | 3 + .../src/main/java/org/teavm/wasm/Example.java | 6 ++ .../main/java/org/teavm/wasm/WasmTarget.java | 10 ++++ .../wasm/generate/WasmGenerationVisitor.java | 34 +++++++++-- .../wasm/intrinsics/WasmAddressIntrinsic.java | 1 + .../org/teavm/wasm/patches/ObjectPatch.java | 59 +++++++++++++++++++ .../main/java/org/teavm/interop/Address.java | 2 + .../main/java/org/teavm/interop/Rename.java | 4 -- 13 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/teavm/runtime/RuntimeJavaObject.java create mode 100644 core/src/main/java/org/teavm/wasm/patches/ObjectPatch.java diff --git a/core/src/main/java/org/teavm/javascript/backend/JavaScriptTarget.java b/core/src/main/java/org/teavm/javascript/backend/JavaScriptTarget.java index b6b36dc01..3321ae007 100644 --- a/core/src/main/java/org/teavm/javascript/backend/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/javascript/backend/JavaScriptTarget.java @@ -49,6 +49,7 @@ import org.teavm.javascript.spi.Injector; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ElementModifier; import org.teavm.model.InstructionLocation; import org.teavm.model.ListableClassHolderSource; @@ -82,6 +83,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private final Set asyncMethods = new HashSet<>(); private final Set asyncFamilyMethods = new HashSet<>(); + @Override + public List getTransformers() { + return Collections.emptyList(); + } + @Override public void setController(TeaVMTargetController controller) { this.controller = controller; diff --git a/core/src/main/java/org/teavm/runtime/RuntimeArray.java b/core/src/main/java/org/teavm/runtime/RuntimeArray.java index ae0297548..d4cb7bad4 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeArray.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeArray.java @@ -15,7 +15,6 @@ */ package org.teavm.runtime; -public class RuntimeArray extends RuntimeObject { - RuntimeObject monitor; +public class RuntimeArray extends RuntimeJavaObject { int size; } diff --git a/core/src/main/java/org/teavm/runtime/RuntimeJavaObject.java b/core/src/main/java/org/teavm/runtime/RuntimeJavaObject.java new file mode 100644 index 000000000..33a73fe5e --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/RuntimeJavaObject.java @@ -0,0 +1,21 @@ +/* + * 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.runtime; + +public class RuntimeJavaObject extends RuntimeObject { + public static int nextId = 1; + public RuntimeObject monitor; +} diff --git a/core/src/main/java/org/teavm/runtime/RuntimeObject.java b/core/src/main/java/org/teavm/runtime/RuntimeObject.java index 339ee9f9b..882a53108 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeObject.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeObject.java @@ -18,5 +18,8 @@ package org.teavm.runtime; import org.teavm.interop.Structure; public class RuntimeObject extends Structure { + public static final int GC_MARKED = 0x80000000; + public static final int MONITOR_EXISTS = 0x20000000; + public int classReference; } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 205496dc9..ab8c06696 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -134,6 +134,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } }; + for (ClassHolderTransformer transformer : target.getTransformers()) { + dependencyChecker.addClassTransformer(transformer); + } + for (TeaVMHostExtension extension : target.getHostExtensions()) { for (Class extensionType : getExtensionTypes(extension)) { extensions.put(extensionType, extension); diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 496757937..0eea40d64 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -18,10 +18,13 @@ package org.teavm.vm; import java.io.OutputStream; import java.util.List; import org.teavm.dependency.DependencyChecker; +import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ListableClassHolderSource; import org.teavm.vm.spi.TeaVMHostExtension; public interface TeaVMTarget { + List getTransformers(); + void setController(TeaVMTargetController controller); List getHostExtensions(); diff --git a/core/src/main/java/org/teavm/wasm/Example.java b/core/src/main/java/org/teavm/wasm/Example.java index 61adbdc93..8f29dd39d 100644 --- a/core/src/main/java/org/teavm/wasm/Example.java +++ b/core/src/main/java/org/teavm/wasm/Example.java @@ -60,6 +60,12 @@ public final class Example { Initialized.foo(); Initialized.foo(); Initialized.foo(); + + Object o = new Object(); + WasmRuntime.print(o.hashCode()); + WasmRuntime.print(o.hashCode()); + WasmRuntime.print(new Object().hashCode()); + WasmRuntime.print(new Object().hashCode()); } private static Base instance(int index) { diff --git a/core/src/main/java/org/teavm/wasm/WasmTarget.java b/core/src/main/java/org/teavm/wasm/WasmTarget.java index ad0fe8eaa..dfc73d73f 100644 --- a/core/src/main/java/org/teavm/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/wasm/WasmTarget.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -33,6 +34,7 @@ import org.teavm.interop.StaticInit; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; @@ -84,6 +86,7 @@ import org.teavm.wasm.model.expression.WasmIntType; import org.teavm.wasm.model.expression.WasmLoadInt32; import org.teavm.wasm.model.expression.WasmReturn; import org.teavm.wasm.model.expression.WasmStoreInt32; +import org.teavm.wasm.patches.ObjectPatch; import org.teavm.wasm.render.WasmRenderer; public class WasmTarget implements TeaVMTarget { @@ -104,6 +107,13 @@ public class WasmTarget implements TeaVMTarget { return true; } + @Override + public List getTransformers() { + List transformers = new ArrayList<>(); + transformers.add(new ObjectPatch()); + return transformers; + } + @Override public void contributeDependencies(DependencyChecker dependencyChecker) { for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) { diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java index 61212b61b..ca8ec3b7e 100644 --- a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java @@ -62,9 +62,7 @@ import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.VariableExpr; import org.teavm.ast.WhileStatement; import org.teavm.interop.Address; -import org.teavm.model.ClassReader; import org.teavm.model.FieldReference; -import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; @@ -111,6 +109,7 @@ import org.teavm.wasm.model.expression.WasmStoreFloat64; import org.teavm.wasm.model.expression.WasmStoreInt32; import org.teavm.wasm.model.expression.WasmStoreInt64; import org.teavm.wasm.model.expression.WasmSwitch; +import org.teavm.wasm.model.expression.WasmUnreachable; class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmGenerationContext context; @@ -735,6 +734,23 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { call.getArguments().add(result); } result = call; + } else if (expr.getType() == InvocationType.CONSTRUCTOR) { + WasmBlock block = new WasmBlock(false); + WasmLocal tmp = function.getLocalVariables().get(getTemporaryInt32()); + block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName()))); + + String methodName = WasmMangling.mangleMethod(expr.getMethod()); + WasmCall call = new WasmCall(methodName); + call.getArguments().add(new WasmGetLocal(tmp)); + for (Expr argument : expr.getArguments()) { + argument.acceptVisitor(this); + call.getArguments().add(result); + } + block.getBody().add(call); + + block.getBody().add(new WasmGetLocal(tmp)); + + result = block; } else { expr.getArguments().get(0).acceptVisitor(this); WasmExpression instance = result; @@ -860,12 +876,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(NewExpr expr) { - int tag = classGenerator.getClassPointer(ValueType.object(expr.getConstructedClass())); + result = allocateObject(expr.getConstructedClass()); + } + + private WasmExpression allocateObject(String className) { + int tag = classGenerator.getClassPointer(ValueType.object(className)); String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate", RuntimeClass.class, Address.class)); WasmCall call = new WasmCall(allocName); call.getArguments().add(new WasmInt32Constant(tag)); - result = call; + return call; } @Override @@ -952,7 +972,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(ThrowStatement statement) { + WasmBlock block = new WasmBlock(false); + statement.getException().acceptVisitor(this); + block.getBody().add(result); + block.getBody().add(new WasmUnreachable()); + + result = block; } @Override diff --git a/core/src/main/java/org/teavm/wasm/intrinsics/WasmAddressIntrinsic.java b/core/src/main/java/org/teavm/wasm/intrinsics/WasmAddressIntrinsic.java index e0e19028f..dd85623ac 100644 --- a/core/src/main/java/org/teavm/wasm/intrinsics/WasmAddressIntrinsic.java +++ b/core/src/main/java/org/teavm/wasm/intrinsics/WasmAddressIntrinsic.java @@ -53,6 +53,7 @@ public class WasmAddressIntrinsic implements WasmIntrinsic { return new WasmConversion(WasmType.INT32, WasmType.INT64, false, value); } case "fromInt": + case "ofObject": return manager.generate(invocation.getArguments().get(0)); case "fromLong": { WasmExpression value = manager.generate(invocation.getArguments().get(0)); diff --git a/core/src/main/java/org/teavm/wasm/patches/ObjectPatch.java b/core/src/main/java/org/teavm/wasm/patches/ObjectPatch.java new file mode 100644 index 000000000..517409d43 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/patches/ObjectPatch.java @@ -0,0 +1,59 @@ +/* + * 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.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/interop/core/src/main/java/org/teavm/interop/Address.java b/interop/core/src/main/java/org/teavm/interop/Address.java index c89c599c2..ea07e251e 100644 --- a/interop/core/src/main/java/org/teavm/interop/Address.java +++ b/interop/core/src/main/java/org/teavm/interop/Address.java @@ -57,4 +57,6 @@ public final class Address { public static native Address fromInt(int value); public static native Address fromLong(long value); + + public static native Address ofObject(Object obj); } diff --git a/interop/core/src/main/java/org/teavm/interop/Rename.java b/interop/core/src/main/java/org/teavm/interop/Rename.java index 7027fde15..a1d6fd04e 100644 --- a/interop/core/src/main/java/org/teavm/interop/Rename.java +++ b/interop/core/src/main/java/org/teavm/interop/Rename.java @@ -17,10 +17,6 @@ package org.teavm.interop; import java.lang.annotation.*; -/** - * - * @author Alexey Andreev - */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) public @interface Rename {