Skip to content

Commit

Permalink
wasm gc: fix support of legacy Object.cast method
Browse files Browse the repository at this point in the history
  • Loading branch information
konsoletyper committed Oct 2, 2024
1 parent 551f050 commit 0bd7bc6
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public void submitMethod(MethodReference methodRef, Program program) {
dep.used = false;
lock(dep, false);
deferredTasks.add(() -> {
classSource.getReferenceResolver().use(dep.method.getReference(), diagnostics);
classSource.getReferenceResolver().use(dep.method.getReference());
processMethod(dep);
dep.used = true;
});
Expand Down Expand Up @@ -476,9 +476,9 @@ private MethodDependency createMethodDep(MethodReference methodRef, MethodHolder
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);

void scheduleMethodAnalysis(MethodDependency dep) {
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
classSource.getReferenceResolver().use(dep.getReference());
deferredTasks.add(() -> {
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
classSource.getReferenceResolver().use(dep.getReference());
processMethod(dep);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class DependencyClassSource implements ClassHolderSource {
this.diagnostics = diagnostics;
innerHierarchy = new ClassHierarchy(innerSource);
this.dependencyRegistration = dependencyRegistration;
referenceResolver = new ReferenceResolver(this, platformTags);
referenceResolver = new ReferenceResolver(this, platformTags, diagnostics);
classInitInsertion = new ClassInitInsertion(this);
}

Expand Down Expand Up @@ -125,6 +125,9 @@ private void transformClass(ClassHolder cls) {
if (method.getProgram() != null) {
var program = method.getProgram();
method.setProgramSupplier(m -> {
if (disposed) {
return null;
}
referenceResolver.resolve(m, program);
classInitInsertion.apply(m, program);
return program;
Expand Down
21 changes: 14 additions & 7 deletions core/src/main/java/org/teavm/dependency/ReferenceResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.SupportedOn;
import org.teavm.interop.UnsupportedOn;
Expand Down Expand Up @@ -64,27 +63,31 @@
public class ReferenceResolver {
private ClassReaderSource classSource;
private MethodReference currentMethod;
private Diagnostics diagnostics;
private Program program;
private boolean modified;
private List<Instruction> instructionsToAdd = new ArrayList<>();
private Map<FieldReference, FieldWrapper> fieldCache = new HashMap<>();
private Map<String, Map<MethodDescriptor, Optional<MethodReader>>> methodCache = new HashMap<>(1000, 0.5f);
private Set<String> platformTags = new HashSet<>();
private UnreachableBasicBlockEliminator unreachableBlockEliminator;
private Map<MethodReference, List<Consumer<Diagnostics>>> pendingErrors = new HashMap<>();
private Map<MethodReference, List<Runnable>> pendingErrors = new HashMap<>();
private Set<MethodReference> usedMethods = new HashSet<>();
private boolean shouldStop;

public ReferenceResolver(ClassReaderSource classSource, String[] platformTags) {
public ReferenceResolver(ClassReaderSource classSource, String[] platformTags, Diagnostics diagnostics) {
this.classSource = classSource;
this.platformTags.addAll(List.of(platformTags));
unreachableBlockEliminator = new UnreachableBasicBlockEliminator();
this.diagnostics = diagnostics;
}

public void use(MethodReference method, Diagnostics diagnostics) {
public void use(MethodReference method) {
usedMethods.add(method);
var errors = pendingErrors.remove(method);
if (errors != null) {
for (var error : errors) {
error.accept(diagnostics);
error.run();
}
}
}
Expand Down Expand Up @@ -394,8 +397,12 @@ private void emitExceptionThrow(TextLocation location, String exceptionName, Str

private void reportError(TextLocation location, String message, Object param) {
var method = currentMethod;
pendingErrors.computeIfAbsent(method, k -> new ArrayList<>()).add(diagnostics ->
diagnostics.error(new CallLocation(method, location), message, param));
if (usedMethods.contains(method)) {
diagnostics.error(new CallLocation(method, location), message, param);
} else {
pendingErrors.computeIfAbsent(method, k -> new ArrayList<>()).add(() ->
diagnostics.error(new CallLocation(method, location), message, param));
}
}

private static class FieldWrapper {
Expand Down
14 changes: 12 additions & 2 deletions core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ TeaVM.wasm = function() {
let getGlobalName = function(name) {
return eval(name);
}
let setGlobalName = function(name, value) {
new Function("value", name + " = value;")(value);
}

function defaults(imports) {
dateImports(imports);
Expand Down Expand Up @@ -146,6 +149,13 @@ TeaVM.wasm = function() {
function isIdentifierPart(s) {
return isIdentifierStart(s) || s >= '0' && s <= '9';
}
function setProperty(obj, prop, value) {
if (obj === null) {
setGlobalName(prop, value);
} else {
obj[prop] = value;
}
}
imports.teavmJso = {
emptyString: () => "",
stringFromCharCode: code => String.fromCharCode(code),
Expand All @@ -158,8 +168,8 @@ TeaVM.wasm = function() {
wrapBoolean: value => !!value,
getProperty: (obj, prop) => obj !== null ? obj[prop] : getGlobalName(prop),
getPropertyPure: (obj, prop) => obj !== null ? obj[prop] : getGlobalName(prop),
setProperty: (obj, prop, value) => obj[prop] = value,
setPropertyPure: (obj, prop) => obj[prop] = value,
setProperty: setProperty,
setPropertyPure: setProperty,
global: getGlobalName,
createClass(name) {
let fn = new Function(
Expand Down
1 change: 1 addition & 0 deletions jso/core/src/main/java/org/teavm/jso/JSObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
*/
public interface JSObject {
@SuppressWarnings("unchecked")
@Deprecated
default <T extends JSObject> T cast() {
return (T) this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ private void createExposedClass(ClassReader cls, ExposedClass exposedCls) {
}

private boolean isJavaScriptClass(ClassReader cls) {
if (typeHelper.isJavaScriptClass(cls.getName())) {
return true;
}
if (cls.getParent() != null && typeHelper.isJavaScriptClass(cls.getParent())) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ public ClassWithConstructor() {
@JSTopLevel
@JSProperty
public static native void setTopLevelProperty(String value);

@JSTopLevel
public static native JSObject createClass(boolean subclass);
}
9 changes: 8 additions & 1 deletion tests/src/test/java/org/teavm/jso/test/ImportClassTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

@RunWith(TeaVMTestRunner.class)
@SkipJVM
@OnlyPlatform(TestPlatform.JAVASCRIPT)
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
@EachTestCompiledSeparately
public class ImportClassTest {
@Test
Expand Down Expand Up @@ -75,6 +75,13 @@ public void topLevel() {
assertEquals("update2", ClassWithConstructor.getTopLevelProperty());
}

@Test
@AttachJavaScript("org/teavm/jso/test/classWithConstructor.js")
public void legacyCastMethod() {
SubclassWithConstructor o = ClassWithConstructor.createClass(true).cast();
assertEquals("subclass", o.baz());
}

@JSBody(script = "return {};")
private static native O create();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2024 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.jso.test;

import org.teavm.jso.JSClass;

@JSClass
public class SubclassWithConstructor extends ClassWithConstructor {
public native String baz();
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ class ClassWithConstructor {
}
}

class SubclassWithConstructor extends ClassWithConstructor {
constructor(foo) {
super(foo);
}

baz() {
return "subclass";
}
}

function createClass(subclass) {
return subclass ? new SubclassWithConstructor(23) : new ClassWithConstructor(42);
}

function topLevelFunction() {
return "top level";
}
Expand Down
4 changes: 3 additions & 1 deletion tools/browser-runner/src/main/resources/test-server/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ window.addEventListener("message", event => {

case "WASM_GC": {
const runtimeFile = request.file.path + "-runtime.js";
appendFiles([{ path: runtimeFile, type: "regular" }], 0, () => {
const runtimeFileObj = { path: runtimeFile, type: "regular" };
const files = request.additionalFiles ? [...request.additionalFiles, runtimeFileObj] : [runtimeFileObj]
appendFiles(files, 0, () => {
launchWasmGCTest(request.file, request.argument, response => {
event.source.postMessage(response, "*");
});
Expand Down

0 comments on commit 0bd7bc6

Please sign in to comment.