mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Wasm: add parser for own debug information
This commit is contained in:
parent
56929b2085
commit
d1feec7ae6
|
@ -24,6 +24,7 @@ public class DebugClassesBuilder extends DebugSectionBuilder implements DebugCla
|
|||
private ObjectIntMap<String> classes = new ObjectIntHashMap<>();
|
||||
|
||||
public DebugClassesBuilder(DebugPackages packages, DebugStrings strings) {
|
||||
super(DebugConstants.SECTION_CLASSES);
|
||||
this.packages = packages;
|
||||
this.strings = strings;
|
||||
}
|
||||
|
|
|
@ -25,4 +25,11 @@ public final class DebugConstants {
|
|||
public static final int LOC_FILE = 3;
|
||||
public static final int LOC_PTR = 4;
|
||||
public static final int LOC_USER = 10;
|
||||
|
||||
public static final String SECTION_STRINGS = "teavm_str";
|
||||
public static final String SECTION_FILES = "teavm_file";
|
||||
public static final String SECTION_PACKAGES = "teavm_pkg";
|
||||
public static final String SECTION_CLASSES = "teavm_cls";
|
||||
public static final String SECTION_METHODS = "teavm_mtd";
|
||||
public static final String SECTION_LINES = "teavm_line";
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class DebugFilesBuilder extends DebugSectionBuilder implements DebugFiles
|
|||
private ObjectIntMap<FileData> fileMap = new ObjectIntHashMap<>();
|
||||
|
||||
public DebugFilesBuilder(DebugStrings strings) {
|
||||
super(DebugConstants.SECTION_FILES);
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,19 +62,19 @@ public class DebugInfoBuilder {
|
|||
|
||||
public List<WasmCustomSection> build() {
|
||||
var result = new ArrayList<WasmCustomSection>();
|
||||
addSection(result, "teavm_str", strings);
|
||||
addSection(result, "teavm_file", files);
|
||||
addSection(result, "teavm_pkg", packages);
|
||||
addSection(result, "teavm_classes", classes);
|
||||
addSection(result, "teavm_methods", methods);
|
||||
addSection(result, "teavm_line", lines);
|
||||
addSection(result, strings);
|
||||
addSection(result, files);
|
||||
addSection(result, packages);
|
||||
addSection(result, classes);
|
||||
addSection(result, methods);
|
||||
addSection(result, lines);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addSection(List<WasmCustomSection> sections, String name, DebugSectionBuilder builder) {
|
||||
private void addSection(List<WasmCustomSection> sections, DebugSectionBuilder builder) {
|
||||
if (builder.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sections.add(new WasmCustomSection(name, builder.build()));
|
||||
sections.add(new WasmCustomSection(builder.name(), builder.build()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ public class DebugLinesBuilder extends DebugSectionBuilder implements DebugLines
|
|||
private Deque<State> states = new ArrayDeque<>();
|
||||
|
||||
public DebugLinesBuilder(DebugFiles files, DebugMethods methods) {
|
||||
super(DebugConstants.SECTION_LINES);
|
||||
this.files = files;
|
||||
this.methods = methods;
|
||||
}
|
||||
|
@ -45,8 +46,8 @@ public class DebugLinesBuilder extends DebugSectionBuilder implements DebugLines
|
|||
@Override
|
||||
public void location(String file, int line) {
|
||||
if (Objects.equals(file, this.file) && this.ptr != lastWrittenPtr && this.line != line) {
|
||||
if (this.ptr - lastWrittenPtr < 32 && Math.abs(this.line - line) <= 3) {
|
||||
blob.writeByte(DebugConstants.LOC_USER + 32 * (this.ptr - lastWrittenPtr) + (line - this.line) + 3);
|
||||
if (this.ptr - lastWrittenPtr < 32 && Math.abs(line - this.line) <= 3) {
|
||||
blob.writeByte(DebugConstants.LOC_USER + (this.ptr - lastWrittenPtr) + 32 * (line - this.line + 3));
|
||||
this.line = line;
|
||||
lastWrittenPtr = ptr;
|
||||
return;
|
||||
|
|
|
@ -25,6 +25,7 @@ public class DebugMethodsBuilder extends DebugSectionBuilder implements DebugMe
|
|||
private ObjectIntMap<MethodReference> methods = new ObjectIntHashMap<>();
|
||||
|
||||
public DebugMethodsBuilder(DebugClasses classes, DebugStrings strings) {
|
||||
super(DebugConstants.SECTION_METHODS);
|
||||
this.classes = classes;
|
||||
this.strings = strings;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class DebugPackagesBuilder extends DebugSectionBuilder implements DebugPa
|
|||
private ObjectIntMap<PackageInfo> packages = new ObjectIntHashMap<>();
|
||||
|
||||
public DebugPackagesBuilder(DebugStrings strings) {
|
||||
super(DebugConstants.SECTION_PACKAGES);
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,21 @@ import org.teavm.backend.wasm.blob.BinaryDataConsumer;
|
|||
import org.teavm.backend.wasm.blob.Blob;
|
||||
|
||||
public class DebugSectionBuilder {
|
||||
private String name;
|
||||
protected Blob blob = new Blob();
|
||||
|
||||
protected DebugSectionBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void read(BinaryDataConsumer consumer) {
|
||||
blob.newReader(consumer).readRemaining();
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public byte[] build() {
|
||||
return blob.toArray();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ import java.nio.charset.StandardCharsets;
|
|||
public class DebugStringsBuilder extends DebugSectionBuilder implements DebugStrings {
|
||||
private ObjectIntMap<String> strings = new ObjectIntHashMap<>();
|
||||
|
||||
public DebugStringsBuilder() {
|
||||
super(DebugConstants.SECTION_STRINGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stringPtr(String str) {
|
||||
var result = strings.getOrDefault(str, -1);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public abstract class ClassInfo {
|
||||
public abstract PackageInfo pkg();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public String fullName() {
|
||||
var pkg = pkg();
|
||||
return pkg != null ? pkg.fullName() + "." + name() : name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public abstract class FileInfo {
|
||||
public abstract FileInfo parent();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public String fullName() {
|
||||
var parent = this.parent();
|
||||
return parent != null ? parent.fullName() + "/" + name() : name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class InliningLocation {
|
||||
private Location location;
|
||||
private MethodInfo method;
|
||||
|
||||
public InliningLocation(Location location, MethodInfo method) {
|
||||
this.location = location;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public Location location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public MethodInfo method() {
|
||||
return method;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class InstructionLocation {
|
||||
private int address;
|
||||
private Location location;
|
||||
|
||||
public InstructionLocation(int address, Location location) {
|
||||
this.address = address;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public int address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public Location location() {
|
||||
return location;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.common.CollectionUtil;
|
||||
|
||||
public class LineInfo {
|
||||
private LineInfoSequence[] sequences;
|
||||
private List<? extends LineInfoSequence> sequenceList;
|
||||
|
||||
public LineInfo(LineInfoSequence[] sequences) {
|
||||
this.sequences = sequences.clone();
|
||||
sequenceList = Collections.unmodifiableList(Arrays.asList(this.sequences));
|
||||
}
|
||||
|
||||
public List<? extends LineInfoSequence> sequences() {
|
||||
return sequenceList;
|
||||
}
|
||||
|
||||
public LineInfoSequence find(int address) {
|
||||
var index = CollectionUtil.binarySearch(sequenceList, address, LineInfoSequence::endAddress);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
if (index >= sequenceList.size()) {
|
||||
return null;
|
||||
}
|
||||
var sequence = sequenceList.get(index);
|
||||
return address >= sequence.startAddress() ? sequence : null;
|
||||
}
|
||||
|
||||
public void dump(PrintStream out) {
|
||||
for (var i = 0; i < sequences.length; ++i) {
|
||||
var sequence = sequences[i];
|
||||
out.println("Sequence " + i + ": " + sequence.method().fullName() + " [" + sequence.startAddress() + ".."
|
||||
+ sequence.endAddress() + ")");
|
||||
for (var location : sequence.unpack().locations()) {
|
||||
out.println(" at " + location.address() + " " + location.location().file().fullName() + ":"
|
||||
+ location.location().line());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public abstract class LineInfoCommand {
|
||||
private int address;
|
||||
|
||||
public LineInfoCommand(int address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public int address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public abstract void acceptVisitor(LineInfoCommandVisitor visitor);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class LineInfoCommandExecutor implements LineInfoCommandVisitor {
|
||||
private FileInfo file;
|
||||
private int line = 1;
|
||||
private int address;
|
||||
private InliningLocation inliningLocation;
|
||||
|
||||
public InstructionLocation createLocation() {
|
||||
return file != null ? new InstructionLocation(address, new Location(file, line, inliningLocation)) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(LineInfoEnterCommand command) {
|
||||
address = command.address();
|
||||
inliningLocation = new InliningLocation(new Location(file, line, inliningLocation), command.method());
|
||||
file = null;
|
||||
line = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(LineInfoExitCommand command) {
|
||||
address = command.address();
|
||||
inliningLocation = inliningLocation.location().inlining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(LineInfoFileCommand command) {
|
||||
address = command.address();
|
||||
file = command.file();
|
||||
line = command.line();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(LineInfoLineCommand command) {
|
||||
address = command.address();
|
||||
line = command.line();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public interface LineInfoCommandVisitor {
|
||||
void visit(LineInfoEnterCommand command);
|
||||
|
||||
void visit(LineInfoExitCommand command);
|
||||
|
||||
void visit(LineInfoFileCommand command);
|
||||
|
||||
void visit(LineInfoLineCommand command);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class LineInfoEnterCommand extends LineInfoCommand {
|
||||
private MethodInfo method;
|
||||
|
||||
public LineInfoEnterCommand(int address, MethodInfo method) {
|
||||
super(address);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public MethodInfo method() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class LineInfoExitCommand extends LineInfoCommand {
|
||||
public LineInfoExitCommand(int address) {
|
||||
super(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class LineInfoFileCommand extends LineInfoCommand {
|
||||
private FileInfo file;
|
||||
private int line;
|
||||
|
||||
public LineInfoFileCommand(int address, FileInfo file, int line) {
|
||||
super(address);
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
}
|
||||
|
||||
public FileInfo file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class LineInfoLineCommand extends LineInfoCommand {
|
||||
private int line;
|
||||
|
||||
public LineInfoLineCommand(int address, int line) {
|
||||
super(address);
|
||||
this.line = line;
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LineInfoSequence {
|
||||
private int startAddress;
|
||||
private int endAddress;
|
||||
private MethodInfo method;
|
||||
private LineInfoCommand[] commands;
|
||||
private List<LineInfoCommand> commandList;
|
||||
|
||||
public LineInfoSequence(int startAddress, int endAddress, MethodInfo method, LineInfoCommand[] commands) {
|
||||
this.startAddress = startAddress;
|
||||
this.endAddress = endAddress;
|
||||
this.method = method;
|
||||
this.commands = commands.clone();
|
||||
commandList = Collections.unmodifiableList(Arrays.asList(this.commands));
|
||||
}
|
||||
|
||||
public int startAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int endAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public MethodInfo method() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public List<? extends LineInfoCommand> commands() {
|
||||
return commandList;
|
||||
}
|
||||
|
||||
public LineInfoUnpackedSequence unpack() {
|
||||
var commandExecutor = new LineInfoCommandExecutor();
|
||||
var locations = new ArrayList<InstructionLocation>();
|
||||
for (var command : commands) {
|
||||
command.acceptVisitor(commandExecutor);
|
||||
locations.add(commandExecutor.createLocation());
|
||||
}
|
||||
return new LineInfoUnpackedSequence(startAddress, endAddress, method, locations);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.common.CollectionUtil;
|
||||
|
||||
public class LineInfoUnpackedSequence {
|
||||
private int startAddress;
|
||||
private int endAddress;
|
||||
private MethodInfo method;
|
||||
private List<? extends InstructionLocation> locations;
|
||||
|
||||
LineInfoUnpackedSequence(int startAddress, int endAddress, MethodInfo method,
|
||||
List<? extends InstructionLocation> locations) {
|
||||
this.startAddress = startAddress;
|
||||
this.endAddress = endAddress;
|
||||
this.method = method;
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
public int startAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int endAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public MethodInfo method() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public List<? extends InstructionLocation> locations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
public InstructionLocation find(int address) {
|
||||
if (address < startAddress || address >= endAddress) {
|
||||
return null;
|
||||
}
|
||||
var index = CollectionUtil.binarySearch(locations, address, InstructionLocation::address);
|
||||
if (index < 0) {
|
||||
index = -index;
|
||||
}
|
||||
return locations.get(Math.min(locations.size() - 1, index));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public class Location {
|
||||
private FileInfo file;
|
||||
private int line;
|
||||
private InliningLocation inlining;
|
||||
|
||||
public Location(FileInfo file, int line, InliningLocation inlining) {
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
this.inlining = inlining;
|
||||
}
|
||||
|
||||
public FileInfo file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public InliningLocation inlining() {
|
||||
return inlining;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public abstract class MethodInfo {
|
||||
public abstract ClassInfo cls();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public String fullName() {
|
||||
return cls().fullName() + "." + name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.info;
|
||||
|
||||
public abstract class PackageInfo {
|
||||
public abstract PackageInfo parent();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public final String fullName() {
|
||||
var parent = this.parent();
|
||||
return parent != null ? parent.fullName() + "." + name() : name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
class BlobReader {
|
||||
private byte[] data;
|
||||
|
||||
public BlobReader(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
import org.teavm.backend.wasm.debug.info.ClassInfo;
|
||||
import org.teavm.backend.wasm.debug.info.PackageInfo;
|
||||
|
||||
public class DebugClassParser extends DebugSectionParser {
|
||||
private DebugStringParser strings;
|
||||
private DebugPackageParser packages;
|
||||
private ClassInfoImpl[] classes;
|
||||
|
||||
public DebugClassParser(DebugStringParser strings, DebugPackageParser packages) {
|
||||
super(DebugConstants.SECTION_CLASSES, strings, packages);
|
||||
this.strings = strings;
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public ClassInfo getClass(int ptr) {
|
||||
return classes[ptr];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
var classes = new ArrayList<ClassInfoImpl>();
|
||||
while (ptr < data.length) {
|
||||
var pkg = packages.getPackage(readLEB());
|
||||
var name = strings.getString(readLEB());
|
||||
classes.add(new ClassInfoImpl(pkg, name));
|
||||
}
|
||||
this.classes = classes.toArray(new ClassInfoImpl[0]);
|
||||
}
|
||||
|
||||
private static class ClassInfoImpl extends ClassInfo {
|
||||
private PackageInfo pkg;
|
||||
private String name;
|
||||
|
||||
ClassInfoImpl(PackageInfo pkg, String name) {
|
||||
this.pkg = pkg;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo pkg() {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
import org.teavm.backend.wasm.debug.info.FileInfo;
|
||||
|
||||
public class DebugFileParser extends DebugSectionParser {
|
||||
private DebugStringParser strings;
|
||||
private FileInfoImpl[] files;
|
||||
|
||||
public DebugFileParser(DebugStringParser strings) {
|
||||
super(DebugConstants.SECTION_FILES, strings);
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
public FileInfo getFile(int index) {
|
||||
return index > 0 ? files[index - 1] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
var builders = new ArrayList<FileBuilder>();
|
||||
while (ptr < data.length) {
|
||||
var parent = readLEB();
|
||||
var nameIndex = readLEB();
|
||||
var name = (nameIndex & 1) == 0
|
||||
? strings.getString(nameIndex >>> 1)
|
||||
: strings.getString(nameIndex >>> 1) + "." + strings.getString(readLEB());
|
||||
builders.add(new FileBuilder(parent, name));
|
||||
}
|
||||
this.files = new FileInfoImpl[builders.size()];
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
files[i] = new FileInfoImpl(builders.get(i).name);
|
||||
}
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
files[i].parent = getFile(builders.get(i).parent);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileInfoImpl extends FileInfo {
|
||||
private FileInfo parent;
|
||||
private final String name;
|
||||
|
||||
private FileInfoImpl(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfo parent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileBuilder {
|
||||
int parent;
|
||||
String name;
|
||||
|
||||
FileBuilder(int parent, String name) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||
import org.teavm.common.Promise;
|
||||
|
||||
public class DebugInfoParser {
|
||||
private static final int WASM_HEADER_SIZE = 8;
|
||||
private DebugInfoReader reader;
|
||||
private int pos;
|
||||
private int posBefore;
|
||||
private byte[] buffer = new byte[256];
|
||||
private int posInBuffer;
|
||||
private int bufferLimit;
|
||||
private int currentLEB;
|
||||
private int currentLEBShift;
|
||||
private boolean eof;
|
||||
private int bytesInLEB;
|
||||
private int sectionCode;
|
||||
private List<byte[]> chunks = new ArrayList<>();
|
||||
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
|
||||
private DebugLinesParser lines;
|
||||
|
||||
public DebugInfoParser(DebugInfoReader reader) {
|
||||
this.reader = reader;
|
||||
|
||||
var strings = addSection(new DebugStringParser());
|
||||
var files = addSection(new DebugFileParser(strings));
|
||||
var packages = addSection(new DebugPackageParser(strings));
|
||||
var classes = addSection(new DebugClassParser(strings, packages));
|
||||
var methods = addSection(new DebugMethodParser(strings, classes));
|
||||
lines = addSection(new DebugLinesParser(files, methods));
|
||||
}
|
||||
|
||||
private <T extends DebugSectionParser> T addSection(T section) {
|
||||
sectionParsers.put(section.name(), section);
|
||||
return section;
|
||||
}
|
||||
|
||||
public LineInfo getLineInfo() {
|
||||
return lines.getLineInfo();
|
||||
}
|
||||
|
||||
public Promise<Void> parse() {
|
||||
return parseHeader().thenAsync(v -> parseSections());
|
||||
}
|
||||
|
||||
private Promise<Void> parseHeader() {
|
||||
return fillAtLeast(16).thenVoid(v -> {
|
||||
if (remaining() < WASM_HEADER_SIZE) {
|
||||
error("Invalid WebAssembly header");
|
||||
}
|
||||
if (readInt32() != 0x6d736100) {
|
||||
error("Invalid WebAssembly magic number");
|
||||
}
|
||||
if (readInt32() != 1) {
|
||||
error("Unsupported WebAssembly version");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> parseSections() {
|
||||
return readLEB().thenAsync(n -> {
|
||||
if (n == null) {
|
||||
return Promise.VOID;
|
||||
}
|
||||
sectionCode = n;
|
||||
return continueParsingSection().thenAsync(v -> parseSections());
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> continueParsingSection() {
|
||||
return requireLEB("Unexpected end of file reading section length").thenAsync(sectionSize -> {
|
||||
if (sectionCode == 0) {
|
||||
return parseSection(pos + sectionSize);
|
||||
} else {
|
||||
return skip(sectionSize, "Error skipping section " + sectionCode + " of size " + sectionSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> parseSection(int limit) {
|
||||
return readString("Error reading custom section name").thenAsync(name -> {
|
||||
var sectionParser = sectionParsers.get(name);
|
||||
System.out.println("Section '" + name + "': " + (sectionParser != null ? "parsed" : "unknown"));
|
||||
if (sectionParser != null) {
|
||||
return readBytes(limit - pos, "Error reading section '" + name + "' content")
|
||||
.thenVoid(sectionParser::parse);
|
||||
} else {
|
||||
return skip(limit - pos, "Error skipping section '" + name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<String> readString(String error) {
|
||||
return readBytes(error).then(b -> new String(b, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private Promise<byte[]> readBytes(String error) {
|
||||
return requireLEB(error + ": error parsing size")
|
||||
.thenAsync(size -> readBytes(size, error + ": error reading content"));
|
||||
}
|
||||
|
||||
private Promise<byte[]> readBytes(int count, String error) {
|
||||
return readBytesImpl(count, error).then(v -> {
|
||||
var result = new byte[count];
|
||||
var i = 0;
|
||||
for (var chunk : chunks) {
|
||||
System.arraycopy(chunk, 0, result, i, chunk.length);
|
||||
i += chunk.length;
|
||||
}
|
||||
chunks.clear();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> readBytesImpl(int count, String error) {
|
||||
posBefore = pos;
|
||||
var min = Math.min(count, remaining());
|
||||
var chunk = new byte[min];
|
||||
System.arraycopy(buffer, posInBuffer, chunk, 0, min);
|
||||
chunks.add(chunk);
|
||||
pos += min;
|
||||
posInBuffer += min;
|
||||
if (count > min) {
|
||||
if (eof) {
|
||||
error(error);
|
||||
}
|
||||
return fill().thenAsync(x -> readBytesImpl(count - min, error));
|
||||
} else {
|
||||
return Promise.VOID;
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<Integer> requireLEB(String errorMessage) {
|
||||
return readLEB().then(n -> {
|
||||
if (n == null) {
|
||||
error(errorMessage);
|
||||
}
|
||||
return n;
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Integer> readLEB() {
|
||||
posBefore = pos;
|
||||
currentLEB = 0;
|
||||
bytesInLEB = 0;
|
||||
currentLEBShift = 0;
|
||||
return continueLEB();
|
||||
}
|
||||
|
||||
private Promise<Integer> continueLEB() {
|
||||
while (posInBuffer < bufferLimit) {
|
||||
var b = buffer[posInBuffer++];
|
||||
++pos;
|
||||
++bytesInLEB;
|
||||
var digit = b & 0x7F;
|
||||
if (((digit << currentLEBShift) >> currentLEBShift) != digit) {
|
||||
error("LEB represents too big number");
|
||||
}
|
||||
currentLEB |= digit << currentLEBShift;
|
||||
if ((b & 0x80) == 0) {
|
||||
return Promise.of(currentLEB);
|
||||
}
|
||||
currentLEBShift += 7;
|
||||
}
|
||||
return fill().thenAsync(bytesRead -> {
|
||||
if (eof) {
|
||||
if (bytesInLEB > 0) {
|
||||
error("Unexpected end of file reached reading LEB");
|
||||
}
|
||||
return Promise.of(null);
|
||||
} else {
|
||||
return continueLEB();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int readInt32() {
|
||||
posBefore = pos;
|
||||
var result = (buffer[posInBuffer] & 255)
|
||||
| ((buffer[posInBuffer + 1] & 255) << 8)
|
||||
| ((buffer[posInBuffer + 2] & 255) << 16)
|
||||
| ((buffer[posInBuffer + 3] & 255) << 24);
|
||||
posInBuffer += 4;
|
||||
pos += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
private int remaining() {
|
||||
return bufferLimit - posInBuffer;
|
||||
}
|
||||
|
||||
private Promise<Void> skip(int bytes, String error) {
|
||||
if (bytes <= remaining()) {
|
||||
posInBuffer += bytes;
|
||||
pos += bytes;
|
||||
return Promise.VOID;
|
||||
} else {
|
||||
posBefore = pos;
|
||||
pos += remaining();
|
||||
bytes -= remaining();
|
||||
posInBuffer = 0;
|
||||
bufferLimit = 0;
|
||||
return skipImpl(bytes, error);
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<Void> skipImpl(int bytes, String error) {
|
||||
return reader.skip(bytes).thenAsync(bytesSkipped -> {
|
||||
if (bytesSkipped < 0) {
|
||||
error(error);
|
||||
}
|
||||
pos += bytesSkipped;
|
||||
return bytes > bytesSkipped ? skipImpl(bytes - bytesSkipped, error) : Promise.VOID;
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> fillAtLeast(int least) {
|
||||
return fill().thenAsync(x -> fillAtLeastImpl(least));
|
||||
}
|
||||
|
||||
private Promise<Void> fillAtLeastImpl(int least) {
|
||||
if (eof || least <= remaining()) {
|
||||
return Promise.VOID;
|
||||
}
|
||||
return reader.read(buffer, bufferLimit, Math.min(least, buffer.length - bufferLimit))
|
||||
.thenAsync(bytesRead -> {
|
||||
if (bytesRead < 0) {
|
||||
eof = true;
|
||||
} else {
|
||||
bufferLimit += bytesRead;
|
||||
}
|
||||
return fillAtLeastImpl(least);
|
||||
});
|
||||
}
|
||||
|
||||
private Promise<Void> fill() {
|
||||
return readNext(buffer.length - remaining());
|
||||
}
|
||||
|
||||
private Promise<Void> readNext(int bytes) {
|
||||
if (eof) {
|
||||
return Promise.VOID;
|
||||
}
|
||||
if (posInBuffer > 0 && posInBuffer < bufferLimit) {
|
||||
System.arraycopy(buffer, posInBuffer, buffer, 0, bufferLimit - posInBuffer);
|
||||
}
|
||||
bufferLimit -= posInBuffer;
|
||||
posInBuffer = 0;
|
||||
return reader.read(buffer, bufferLimit, bytes).thenVoid(bytesRead -> {
|
||||
if (bytesRead < 0) {
|
||||
eof = true;
|
||||
} else {
|
||||
bufferLimit += bytesRead;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void error(String message) {
|
||||
throw new ParseException(message, posBefore);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import org.teavm.common.Promise;
|
||||
|
||||
public interface DebugInfoReader {
|
||||
Promise<Integer> skip(int amount);
|
||||
|
||||
Promise<Integer> read(byte[] buffer, int offset, int count);
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
import org.teavm.backend.wasm.debug.info.FileInfo;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoCommand;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoEnterCommand;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoExitCommand;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoFileCommand;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoLineCommand;
|
||||
import org.teavm.backend.wasm.debug.info.LineInfoSequence;
|
||||
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
||||
|
||||
public class DebugLinesParser extends DebugSectionParser {
|
||||
private DebugFileParser files;
|
||||
private DebugMethodParser methods;
|
||||
private LineInfo lineInfo;
|
||||
private List<LineInfoSequence> sequences = new ArrayList<>();
|
||||
private List<LineInfoCommand> commands = new ArrayList<>();
|
||||
private Deque<State> stateStack = new ArrayDeque<>();
|
||||
private FileInfo file;
|
||||
private int line = 1;
|
||||
private int address;
|
||||
private MethodInfo currentMethod;
|
||||
private int sequenceStartAddress;
|
||||
|
||||
public DebugLinesParser(
|
||||
DebugFileParser files,
|
||||
DebugMethodParser methods
|
||||
) {
|
||||
super(DebugConstants.SECTION_LINES, files, methods);
|
||||
this.files = files;
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
public LineInfo getLineInfo() {
|
||||
return lineInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
while (ptr < data.length) {
|
||||
var cmd = data[ptr++] & 0xFF;
|
||||
switch (cmd) {
|
||||
case DebugConstants.LOC_START:
|
||||
start();
|
||||
break;
|
||||
case DebugConstants.LOC_END:
|
||||
end();
|
||||
break;
|
||||
case DebugConstants.LOC_LINE:
|
||||
moveToLine();
|
||||
break;
|
||||
case DebugConstants.LOC_FILE:
|
||||
moveToFile();
|
||||
break;
|
||||
case DebugConstants.LOC_PTR:
|
||||
advanceAddress();
|
||||
break;
|
||||
default:
|
||||
if (cmd >= DebugConstants.LOC_USER) {
|
||||
specialCommand(cmd);
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid command at " + ptr + ": " + cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
lineInfo = new LineInfo(sequences.toArray(new LineInfoSequence[0]));
|
||||
sequences = null;
|
||||
commands = null;
|
||||
stateStack = null;
|
||||
currentMethod = null;
|
||||
}
|
||||
|
||||
private void start() {
|
||||
var method = methods.getMethod(readLEB());
|
||||
if (currentMethod == null) {
|
||||
currentMethod = method;
|
||||
sequenceStartAddress = address;
|
||||
} else {
|
||||
stateStack.push(new State(file, line));
|
||||
commands.add(new LineInfoEnterCommand(address, method));
|
||||
}
|
||||
file = null;
|
||||
line = 1;
|
||||
}
|
||||
|
||||
private void end() {
|
||||
if (stateStack.isEmpty()) {
|
||||
if (currentMethod != null) {
|
||||
sequences.add(new LineInfoSequence(sequenceStartAddress, address, currentMethod,
|
||||
commands.toArray(new LineInfoCommand[0])));
|
||||
}
|
||||
commands.clear();
|
||||
currentMethod = null;
|
||||
file = null;
|
||||
line = 1;
|
||||
} else {
|
||||
var state = stateStack.pop();
|
||||
file = state.file;
|
||||
line = state.line;
|
||||
commands.add(new LineInfoExitCommand(address));
|
||||
}
|
||||
}
|
||||
|
||||
private void moveToLine() {
|
||||
line = line + readSignedLEB();
|
||||
if (currentMethod != null) {
|
||||
commands.add(new LineInfoLineCommand(address, line));
|
||||
}
|
||||
}
|
||||
|
||||
private void moveToFile() {
|
||||
file = files.getFile(readLEB());
|
||||
line = 1;
|
||||
if (ptr < data.length && data[ptr] == DebugConstants.LOC_LINE) {
|
||||
++ptr;
|
||||
line = line + readSignedLEB();
|
||||
}
|
||||
if (currentMethod != null) {
|
||||
commands.add(new LineInfoFileCommand(address, file, line));
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceAddress() {
|
||||
address += readLEB();
|
||||
}
|
||||
|
||||
private void specialCommand(int cmd) {
|
||||
cmd -= DebugConstants.LOC_USER;
|
||||
address += cmd % 32;
|
||||
cmd /= 32;
|
||||
line += cmd - 3;
|
||||
if (currentMethod != null) {
|
||||
commands.add(new LineInfoFileCommand(address, file, line));
|
||||
}
|
||||
}
|
||||
|
||||
private static class State {
|
||||
FileInfo file;
|
||||
int line;
|
||||
|
||||
State(FileInfo file, int line) {
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
import org.teavm.backend.wasm.debug.info.ClassInfo;
|
||||
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
||||
|
||||
public class DebugMethodParser extends DebugSectionParser {
|
||||
private DebugStringParser strings;
|
||||
private DebugClassParser classes;
|
||||
private MethodInfoImpl[] methods;
|
||||
|
||||
public DebugMethodParser(DebugStringParser strings, DebugClassParser classes) {
|
||||
super(DebugConstants.SECTION_METHODS);
|
||||
this.strings = strings;
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
public MethodInfo getMethod(int ptr) {
|
||||
return methods[ptr];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
var methods = new ArrayList<MethodInfoImpl>();
|
||||
while (ptr < data.length) {
|
||||
var cls = classes.getClass(readLEB());
|
||||
var name = strings.getString(readLEB());
|
||||
methods.add(new MethodInfoImpl(cls, name));
|
||||
}
|
||||
this.methods = methods.toArray(new MethodInfoImpl[0]);
|
||||
}
|
||||
|
||||
private static class MethodInfoImpl extends MethodInfo {
|
||||
private ClassInfo cls;
|
||||
private String name;
|
||||
|
||||
MethodInfoImpl(ClassInfo cls, String name) {
|
||||
this.cls = cls;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassInfo cls() {
|
||||
return cls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
import org.teavm.backend.wasm.debug.info.PackageInfo;
|
||||
|
||||
public class DebugPackageParser extends DebugSectionParser {
|
||||
private DebugStringParser strings;
|
||||
private PackageInfoImpl[] packages;
|
||||
|
||||
public DebugPackageParser(DebugStringParser strings) {
|
||||
super(DebugConstants.SECTION_PACKAGES, strings);
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
public PackageInfo getPackage(int index) {
|
||||
return index == 0 ? null : packages[index - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
var packageBuilders = new ArrayList<PackageBuilder>();
|
||||
while (ptr < data.length) {
|
||||
packageBuilders.add(new PackageBuilder(readLEB(), strings.getString(readLEB())));
|
||||
}
|
||||
packages = new PackageInfoImpl[packageBuilders.size()];
|
||||
for (var i = 0; i < packages.length; ++i) {
|
||||
packages[i] = new PackageInfoImpl(packageBuilders.get(i).name);
|
||||
}
|
||||
for (var i = 0; i < packages.length; ++i) {
|
||||
packages[i].parent = getPackage(packageBuilders.get(i).parent);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PackageInfoImpl extends PackageInfo {
|
||||
private PackageInfo parent;
|
||||
private String name;
|
||||
|
||||
PackageInfoImpl(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo parent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PackageBuilder {
|
||||
int parent;
|
||||
String name;
|
||||
|
||||
PackageBuilder(int parent, String name) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class DebugSectionParser {
|
||||
private String name;
|
||||
private List<DebugSectionParser> dependantSections = new ArrayList<>();
|
||||
byte[] data;
|
||||
int ptr;
|
||||
private boolean ready;
|
||||
private int unsatisfiedDependencies;
|
||||
|
||||
protected DebugSectionParser(String name, DebugSectionParser... dependencies) {
|
||||
this.name = name;
|
||||
for (var dependency : dependencies) {
|
||||
dependency.dependantSections.add(this);
|
||||
}
|
||||
unsatisfiedDependencies = dependencies.length;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean ready() {
|
||||
return ready;
|
||||
}
|
||||
|
||||
public void parse(byte[] data) {
|
||||
this.data = data;
|
||||
if (unsatisfiedDependencies == 0) {
|
||||
parse();
|
||||
}
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
doParse();
|
||||
ready = true;
|
||||
data = null;
|
||||
for (var dependant : dependantSections) {
|
||||
if (--dependant.unsatisfiedDependencies == 0 && dependant.data != null) {
|
||||
dependant.parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doParse();
|
||||
|
||||
protected int readLEB() {
|
||||
var result = 0;
|
||||
var shift = 0;
|
||||
while (true) {
|
||||
var b = data[ptr++];
|
||||
result |= (b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected int readSignedLEB() {
|
||||
var result = 0;
|
||||
var shift = 0;
|
||||
while (true) {
|
||||
var b = data[ptr++];
|
||||
result |= (b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
if ((b & 0x40) != 0) {
|
||||
result |= -1 << (shift + 7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String readString() {
|
||||
var length = readLEB();
|
||||
var result = new String(data, ptr, length, StandardCharsets.UTF_8);
|
||||
ptr += length;
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||
|
||||
public class DebugStringParser extends DebugSectionParser {
|
||||
private String[] strings;
|
||||
|
||||
public DebugStringParser() {
|
||||
super(DebugConstants.SECTION_STRINGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
var strings = new ArrayList<String>();
|
||||
while (ptr < data.length) {
|
||||
strings.add(readString());
|
||||
}
|
||||
this.strings = strings.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String getString(int ref) {
|
||||
return strings[ref];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 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.debug.parser;
|
||||
|
||||
class ParseException extends RuntimeException {
|
||||
final String message;
|
||||
final int pos;
|
||||
|
||||
ParseException(String message, int pos) {
|
||||
super("Error parsing at " + pos + ": " + message);
|
||||
this.message = message;
|
||||
this.pos = pos;
|
||||
}
|
||||
}
|
54
core/src/main/java/org/teavm/common/CollectionUtil.java
Normal file
54
core/src/main/java/org/teavm/common/CollectionUtil.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2022 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.common;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class CollectionUtil {
|
||||
private CollectionUtil() {
|
||||
}
|
||||
|
||||
public static <T, K extends Comparable<K>> int binarySearch(List<? extends T> list, K key,
|
||||
Function<T, K> keyExtractor) {
|
||||
return binarySearch(list, key, keyExtractor, Comparable::compareTo);
|
||||
}
|
||||
|
||||
public static <T, K> int binarySearch(List<? extends T> list, K key, Function<T, K> keyExtractor,
|
||||
Comparator<K> comparator) {
|
||||
var l = 0;
|
||||
var u = list.size() - 1;
|
||||
while (true) {
|
||||
var i = (l + u) / 2;
|
||||
var t = keyExtractor.apply(list.get(i));
|
||||
var cmp = comparator.compare(t, key);
|
||||
if (cmp == 0) {
|
||||
return i;
|
||||
} else if (cmp > 0) {
|
||||
l = i + i;
|
||||
if (l > u) {
|
||||
return -i - 1;
|
||||
}
|
||||
} else {
|
||||
u = i - 1;
|
||||
if (u < l) {
|
||||
return -i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,14 +15,17 @@
|
|||
*/
|
||||
package org.teavm.common;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Promise<T> {
|
||||
private static ThreadLocal<Queue<Runnable>> processing = new ThreadLocal<>();
|
||||
public static final Promise<Void> VOID = Promise.of(null);
|
||||
|
||||
private T value;
|
||||
|
@ -187,6 +190,7 @@ public class Promise<T> {
|
|||
}
|
||||
|
||||
<S> void passValue(Function<? super T, S> f, Promise<? super S> target) {
|
||||
runAction(() -> {
|
||||
if (state == State.COMPLETED) {
|
||||
S next;
|
||||
try {
|
||||
|
@ -199,17 +203,28 @@ public class Promise<T> {
|
|||
} else {
|
||||
target.completeWithError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
<S> void passValueAsync(Function<T, Promise<S>> f, Promise<S> target) {
|
||||
runAction(() -> {
|
||||
if (state == State.COMPLETED) {
|
||||
target.completeAsync(f.apply(value));
|
||||
Promise<S> next;
|
||||
try {
|
||||
next = f.apply(value);
|
||||
} catch (Throwable e) {
|
||||
target.completeWithError(e);
|
||||
return;
|
||||
}
|
||||
target.completeAsync(next);
|
||||
} else {
|
||||
target.completeWithError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
<S> void passError(Function<Throwable, S> f, Promise<? super S> target) {
|
||||
runAction(() -> {
|
||||
S next;
|
||||
try {
|
||||
next = f.apply(error);
|
||||
|
@ -218,6 +233,7 @@ public class Promise<T> {
|
|||
return;
|
||||
}
|
||||
target.complete(next);
|
||||
});
|
||||
}
|
||||
|
||||
void complete(T value) {
|
||||
|
@ -245,6 +261,7 @@ public class Promise<T> {
|
|||
}
|
||||
|
||||
private void completeImpl(T value) {
|
||||
runAction(() -> {
|
||||
state = State.COMPLETED;
|
||||
this.value = value;
|
||||
|
||||
|
@ -260,6 +277,7 @@ public class Promise<T> {
|
|||
}
|
||||
}
|
||||
catchList = null;
|
||||
});
|
||||
}
|
||||
|
||||
void completeWithError(Throwable e) {
|
||||
|
@ -270,6 +288,7 @@ public class Promise<T> {
|
|||
}
|
||||
|
||||
void completeWithErrorImpl(Throwable e) {
|
||||
runAction(() -> {
|
||||
state = State.ERRORED;
|
||||
this.error = e;
|
||||
|
||||
|
@ -283,6 +302,22 @@ public class Promise<T> {
|
|||
e.printStackTrace();
|
||||
}
|
||||
thenList = null;
|
||||
});
|
||||
}
|
||||
|
||||
private void runAction(Runnable action) {
|
||||
var queue = processing.get();
|
||||
if (queue != null) {
|
||||
queue.add(action);
|
||||
} else {
|
||||
queue = new ArrayDeque<>();
|
||||
queue.add(action);
|
||||
processing.set(queue);
|
||||
while (!queue.isEmpty()) {
|
||||
queue.remove().run();
|
||||
}
|
||||
processing.remove();
|
||||
}
|
||||
}
|
||||
|
||||
enum State {
|
||||
|
|
Loading…
Reference in New Issue
Block a user