2018-03-27 20:04:35 +02:00
|
|
|
|
/*
|
2022-01-01 21:03:06 +01:00
|
|
|
|
Copyright 2018 - 2022 Volker Berlin (i-net software)
|
2018-03-27 20:04:35 +02:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
*/
|
2018-03-25 12:57:04 +02:00
|
|
|
|
package de.inetsoftware.jwebassembly.module;
|
|
|
|
|
|
2018-08-10 18:52:29 +02:00
|
|
|
|
import java.util.ArrayDeque;
|
2018-03-27 20:04:35 +02:00
|
|
|
|
import java.util.ArrayList;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
import java.util.Arrays;
|
2018-05-20 11:54:06 +02:00
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashMap;
|
2020-03-05 21:31:15 +01:00
|
|
|
|
import java.util.Iterator;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
import java.util.List;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
2018-08-10 18:52:29 +02:00
|
|
|
|
import javax.annotation.Nonnull;
|
2021-02-27 20:40:30 +01:00
|
|
|
|
import javax.annotation.Nullable;
|
2018-08-10 18:52:29 +02:00
|
|
|
|
|
2019-02-23 14:59:43 +01:00
|
|
|
|
import de.inetsoftware.classparser.Code;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
import de.inetsoftware.classparser.CodeInputStream;
|
2021-02-27 20:40:30 +01:00
|
|
|
|
import de.inetsoftware.classparser.ConstantClass;
|
2018-11-03 18:01:42 +01:00
|
|
|
|
import de.inetsoftware.classparser.TryCatchFinally;
|
2018-05-01 11:46:42 +02:00
|
|
|
|
import de.inetsoftware.jwebassembly.WasmException;
|
2020-04-11 17:04:21 +02:00
|
|
|
|
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
|
2019-07-23 18:23:59 +02:00
|
|
|
|
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
|
2019-01-20 19:58:23 +01:00
|
|
|
|
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
2018-12-03 21:09:22 +01:00
|
|
|
|
import de.inetsoftware.jwebassembly.wasm.NumericOperator;
|
|
|
|
|
import de.inetsoftware.jwebassembly.wasm.ValueType;
|
2020-04-11 17:04:21 +02:00
|
|
|
|
import de.inetsoftware.jwebassembly.wasm.VariableOperator;
|
2018-12-03 21:09:22 +01:00
|
|
|
|
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This calculate the goto offsets from Java back to block operations
|
|
|
|
|
*
|
|
|
|
|
* @author Volker Berlin
|
|
|
|
|
*
|
|
|
|
|
*/
|
2022-01-02 18:20:34 +01:00
|
|
|
|
class BranchManager {
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
2018-05-20 11:54:06 +02:00
|
|
|
|
private final ArrayList<ParsedBlock> allParsedOperations = new ArrayList<>();
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2018-05-20 11:54:06 +02:00
|
|
|
|
private final BranchNode root = new BranchNode( 0, Integer.MAX_VALUE, null, null );
|
|
|
|
|
|
|
|
|
|
private final HashMap<Integer, ParsedBlock> loops = new HashMap<>();
|
2018-03-27 20:04:35 +02:00
|
|
|
|
|
2020-04-11 17:04:21 +02:00
|
|
|
|
private final WasmOptions options;
|
|
|
|
|
|
2019-03-02 21:54:27 +01:00
|
|
|
|
private final List<WasmInstruction> instructions;
|
|
|
|
|
|
2020-04-13 16:05:41 +02:00
|
|
|
|
private final LocaleVariableManager localVariables;
|
|
|
|
|
|
2019-03-02 21:54:27 +01:00
|
|
|
|
private TryCatchFinally[] exceptionTable;
|
2018-06-21 18:49:55 +02:00
|
|
|
|
|
2021-10-24 15:29:12 +02:00
|
|
|
|
private final ArrayList<BreakBlock> breakOperations = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
2018-06-21 18:49:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create a branch manager.
|
|
|
|
|
*
|
2020-04-11 17:04:21 +02:00
|
|
|
|
* @param options
|
|
|
|
|
* compiler option/properties
|
2018-06-21 18:49:55 +02:00
|
|
|
|
* @param instructions
|
|
|
|
|
* the target for instructions
|
2020-04-13 16:05:41 +02:00
|
|
|
|
* @param localVariables
|
|
|
|
|
* the local variables
|
2018-06-21 18:49:55 +02:00
|
|
|
|
*/
|
2022-01-02 18:20:34 +01:00
|
|
|
|
public BranchManager( WasmOptions options, List<WasmInstruction> instructions, LocaleVariableManager localVariables ) {
|
2020-04-11 17:04:21 +02:00
|
|
|
|
this.options = options;
|
2020-04-13 16:05:41 +02:00
|
|
|
|
this.instructions = instructions;
|
|
|
|
|
this.localVariables = localVariables;
|
2018-06-21 18:49:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 20:04:35 +02:00
|
|
|
|
/**
|
2018-03-30 17:33:23 +02:00
|
|
|
|
* Remove all branch information for reusing the manager.
|
2019-02-23 14:59:43 +01:00
|
|
|
|
*
|
|
|
|
|
* @param code
|
|
|
|
|
* the Java method code
|
2018-03-27 20:04:35 +02:00
|
|
|
|
*/
|
2019-02-23 14:59:43 +01:00
|
|
|
|
void reset( @Nonnull Code code ) {
|
2018-03-30 17:33:23 +02:00
|
|
|
|
allParsedOperations.clear();
|
|
|
|
|
root.clear();
|
2018-05-20 11:54:06 +02:00
|
|
|
|
loops.clear();
|
2021-10-24 16:19:21 +02:00
|
|
|
|
breakOperations.clear();
|
2021-10-24 15:29:12 +02:00
|
|
|
|
root.endPos = root.elseEndPos = code.getCodeSize();
|
2019-03-02 21:54:27 +01:00
|
|
|
|
exceptionTable = code.getExceptionTable();
|
2018-11-03 18:01:42 +01:00
|
|
|
|
for( TryCatchFinally ex : exceptionTable ) {
|
|
|
|
|
allParsedOperations.add( new TryCatchParsedBlock( ex ) );
|
|
|
|
|
}
|
2018-03-27 20:04:35 +02:00
|
|
|
|
}
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2018-08-03 15:17:07 +02:00
|
|
|
|
* Add a new Java block operator to handle from this manager.
|
2018-03-25 12:57:04 +02:00
|
|
|
|
*
|
2018-03-27 20:04:35 +02:00
|
|
|
|
* @param startPosition
|
2018-03-25 12:57:04 +02:00
|
|
|
|
* the byte position of the start position
|
|
|
|
|
* @param offset
|
|
|
|
|
* the relative jump position
|
2019-03-19 19:35:42 +01:00
|
|
|
|
* @param nextPosition
|
|
|
|
|
* the position of the next instruction
|
2018-05-01 11:46:42 +02:00
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the current line number
|
2018-03-25 12:57:04 +02:00
|
|
|
|
*/
|
2019-03-19 19:35:42 +01:00
|
|
|
|
void addGotoOperator( int startPosition, int offset, int nextPosition, int lineNumber ) {
|
|
|
|
|
allParsedOperations.add( new ParsedBlock( JavaBlockOperator.GOTO, startPosition, offset, nextPosition, lineNumber ) );
|
2018-03-27 20:04:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 20:52:54 +02:00
|
|
|
|
/**
|
2018-08-03 15:17:07 +02:00
|
|
|
|
* Add a new IF operator.
|
|
|
|
|
*
|
|
|
|
|
* @param startPosition
|
|
|
|
|
* the byte position of the start position
|
|
|
|
|
* @param offset
|
|
|
|
|
* the relative jump position
|
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the current line number
|
|
|
|
|
* @param instr
|
|
|
|
|
* the compare instruction
|
|
|
|
|
*/
|
|
|
|
|
void addIfOperator( int startPosition, int offset, int lineNumber, WasmNumericInstruction instr ) {
|
2020-04-12 10:44:53 +02:00
|
|
|
|
JumpInstruction jump = new JumpInstruction( startPosition + offset, 1, null, startPosition, lineNumber );
|
2020-03-26 18:21:50 +01:00
|
|
|
|
instructions.add( jump );
|
|
|
|
|
allParsedOperations.add( new IfParsedBlock( startPosition, offset, lineNumber, instr, jump ) );
|
2018-08-03 15:17:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a new switch block.
|
2018-05-04 20:52:54 +02:00
|
|
|
|
*
|
|
|
|
|
* @param startPosition
|
|
|
|
|
* the byte position of the start position
|
|
|
|
|
* @param offset
|
|
|
|
|
* the relative jump position
|
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the current line number
|
|
|
|
|
* @param keys
|
2018-05-12 09:59:42 +02:00
|
|
|
|
* the values of the cases or null if a tableswitch
|
2018-05-04 20:52:54 +02:00
|
|
|
|
* @param positions
|
|
|
|
|
* the code positions
|
2018-05-07 18:46:15 +02:00
|
|
|
|
* @param defaultPosition
|
|
|
|
|
* the code position of the default block
|
2018-05-04 20:52:54 +02:00
|
|
|
|
*/
|
2018-08-03 15:17:07 +02:00
|
|
|
|
void addSwitchOperator( int startPosition, int offset, int lineNumber, int[] keys, int[] positions, int defaultPosition ) {
|
2018-05-04 20:52:54 +02:00
|
|
|
|
allParsedOperations.add( new SwitchParsedBlock( startPosition, offset, lineNumber, keys, positions, defaultPosition ) );
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 20:04:35 +02:00
|
|
|
|
/**
|
|
|
|
|
* Calculate all block operators from the parsed information.
|
|
|
|
|
*/
|
|
|
|
|
void calculate() {
|
2020-05-25 19:28:47 +02:00
|
|
|
|
List<ParsedBlock> parsedOperations = allParsedOperations;
|
2022-01-29 22:11:21 +01:00
|
|
|
|
addLoops( parsedOperations );
|
2020-05-25 19:28:47 +02:00
|
|
|
|
Collections.sort( parsedOperations );
|
2021-12-26 20:17:15 +01:00
|
|
|
|
normalizeEmptyThenBlocks( parsedOperations );
|
2020-05-25 19:28:47 +02:00
|
|
|
|
calculate( root, parsedOperations );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
for( BreakBlock breakBlock : breakOperations ) {
|
|
|
|
|
calculateBreak( breakBlock );
|
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 11:54:06 +02:00
|
|
|
|
/**
|
|
|
|
|
* In the compiled Java byte code there is no marker for the start of loop. But we need this marker. That we add a
|
|
|
|
|
* virtual loop operator on the target position of GOTO operators with a negative offset.
|
2022-01-29 22:11:21 +01:00
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the parsed operations
|
2018-05-20 11:54:06 +02:00
|
|
|
|
*/
|
2022-01-29 22:11:21 +01:00
|
|
|
|
private void addLoops( List<ParsedBlock> parsedOperations ) {
|
|
|
|
|
for( int b = 0; b < parsedOperations.size(); b++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( b );
|
2018-06-02 23:57:04 +02:00
|
|
|
|
if( parsedBlock.startPosition > parsedBlock.endPosition ) {
|
|
|
|
|
switch ( parsedBlock.op ) {
|
|
|
|
|
case GOTO: // do while(true) loop; Continue
|
|
|
|
|
case IF: // do while(condition) loop
|
|
|
|
|
int start = parsedBlock.endPosition;
|
|
|
|
|
ParsedBlock loop = loops.get( start );
|
|
|
|
|
if( loop == null ) {
|
2019-03-19 19:35:42 +01:00
|
|
|
|
loop = new ParsedBlock( JavaBlockOperator.LOOP, start, 0, start, parsedBlock.lineNumber );
|
2018-06-02 23:57:04 +02:00
|
|
|
|
loops.put( start, loop );
|
2022-01-30 14:34:39 +01:00
|
|
|
|
// if a condition form outside of the loop point inside the loop then it must be conditional return and a jump to the loop condition.
|
|
|
|
|
for( int n = b - 1; n >= 0; n-- ) {
|
|
|
|
|
ParsedBlock prevBlock = parsedOperations.get( n );
|
|
|
|
|
if( prevBlock.op == JavaBlockOperator.IF && prevBlock.startPosition < start && prevBlock.endPosition > start
|
|
|
|
|
&& prevBlock.endPosition < parsedBlock.startPosition ) {
|
|
|
|
|
prevBlock.endPosition = start;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-02 23:57:04 +02:00
|
|
|
|
}
|
2021-11-16 21:28:11 +01:00
|
|
|
|
loop.nextPosition = parsedBlock.startPosition; // Jump position for Continue
|
2019-03-19 19:35:42 +01:00
|
|
|
|
loop.endPosition = parsedBlock.nextPosition;
|
2018-06-02 23:57:04 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2020-04-10 22:22:45 +02:00
|
|
|
|
throw new WasmException( "Unimplemented loop code operation: " + parsedBlock.op, parsedBlock.lineNumber );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
2018-08-02 12:18:31 +02:00
|
|
|
|
} else {
|
|
|
|
|
switch ( parsedBlock.op ) {
|
|
|
|
|
case GOTO:
|
|
|
|
|
// forward GOTO can be a while(condition) loop.
|
|
|
|
|
// while(condition) {
|
|
|
|
|
// ....
|
|
|
|
|
// }
|
2018-08-03 12:50:12 +02:00
|
|
|
|
// will be compiled from Eclipse compiler 4.7 to:
|
2018-08-02 12:18:31 +02:00
|
|
|
|
// GOTO CONDITION:
|
|
|
|
|
// START:
|
|
|
|
|
// ....
|
|
|
|
|
// CONDITION:
|
|
|
|
|
// if<cond> START:
|
|
|
|
|
// we can not match this in WASM because a missing GOTO that we need to move the condition to the start of the loop
|
2019-03-19 19:35:42 +01:00
|
|
|
|
int nextPos = parsedBlock.nextPosition;
|
2022-01-29 22:11:21 +01:00
|
|
|
|
for( int n = b + 1; n < parsedOperations.size(); n++ ) {
|
|
|
|
|
ParsedBlock nextBlock = parsedOperations.get( n );
|
2019-03-21 21:14:21 +01:00
|
|
|
|
if( nextBlock.op == JavaBlockOperator.IF && nextBlock.endPosition == nextPos ) { // Eclipse loop with normal goto
|
2022-01-29 22:11:21 +01:00
|
|
|
|
parsedOperations.remove( n );
|
2022-01-23 16:48:40 +01:00
|
|
|
|
((IfParsedBlock)nextBlock).negateCompare();
|
|
|
|
|
|
2019-03-21 21:14:21 +01:00
|
|
|
|
int conditionStart = parsedBlock.endPosition;
|
|
|
|
|
int conditionEnd = nextBlock.nextPosition;
|
2022-01-23 16:48:40 +01:00
|
|
|
|
if( conditionStart == conditionEnd ) {
|
|
|
|
|
// WHILE loop in ELSE block.
|
|
|
|
|
// The GOTO from the ELSE and the jump to the end of the WHILE loop is merged to one GOTO.
|
|
|
|
|
for( n = b - 1; n >= 0; n-- ) {
|
2022-01-29 22:11:21 +01:00
|
|
|
|
ParsedBlock prevBlock = parsedOperations.get( n );
|
2022-01-23 16:48:40 +01:00
|
|
|
|
if( prevBlock.endPosition > nextPos ) {
|
|
|
|
|
conditionStart = prevBlock.endPosition;
|
|
|
|
|
prevBlock.endPosition = parsedBlock.nextPosition;
|
|
|
|
|
// Create the second GOTO
|
2022-01-29 22:11:21 +01:00
|
|
|
|
parsedOperations.add( b, new ParsedBlock( JavaBlockOperator.GOTO, parsedBlock.startPosition, parsedBlock.endPosition - parsedBlock.startPosition, parsedBlock.nextPosition, parsedBlock.lineNumber ) );
|
2022-01-23 16:48:40 +01:00
|
|
|
|
// we have only 3 code positions but need 6 for two GOTO
|
|
|
|
|
parsedBlock.startPosition = parsedBlock.nextPosition;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-21 21:14:21 +01:00
|
|
|
|
convertToLoop( parsedBlock, conditionStart, conditionEnd );
|
2019-09-25 19:24:01 +02:00
|
|
|
|
|
|
|
|
|
// if conditions that point at the end of the loop for optimization must now point at start.
|
|
|
|
|
for( n = b - 1; n >= 0; n-- ) {
|
2022-01-29 22:11:21 +01:00
|
|
|
|
ParsedBlock prevBlock = parsedOperations.get( n );
|
2019-09-25 19:24:01 +02:00
|
|
|
|
if( prevBlock.op == JavaBlockOperator.IF && prevBlock.endPosition == conditionStart ) {
|
|
|
|
|
prevBlock.endPosition = parsedBlock.startPosition;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-02 12:18:31 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-23 16:48:40 +01:00
|
|
|
|
if( nextBlock.op == JavaBlockOperator.GOTO && nextBlock.endPosition == nextPos ) { // Eclipse loop with wide goto_w
|
2022-01-29 22:11:21 +01:00
|
|
|
|
ParsedBlock prevBlock = parsedOperations.get( n - 1 );
|
2019-03-21 21:14:21 +01:00
|
|
|
|
if( prevBlock.op == JavaBlockOperator.IF && prevBlock.endPosition == nextBlock.nextPosition ) {
|
|
|
|
|
int conditionStart = parsedBlock.endPosition;
|
|
|
|
|
int conditionEnd = prevBlock.nextPosition;
|
2022-01-23 16:48:40 +01:00
|
|
|
|
if( conditionStart >= conditionEnd ) {
|
|
|
|
|
// WHILE loop in ELSE block.
|
|
|
|
|
// occur with java.math.BigInteger.add(int[],int[]) in Java 8
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-03-21 21:14:21 +01:00
|
|
|
|
convertToLoop( parsedBlock, conditionStart, conditionEnd );
|
2022-01-29 22:11:21 +01:00
|
|
|
|
parsedOperations.remove( n );
|
|
|
|
|
parsedOperations.remove( n - 1 );
|
2019-03-21 21:14:21 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-02 12:18:31 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
}
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 22:11:21 +01:00
|
|
|
|
parsedOperations.addAll( loops.values() );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 21:14:21 +01:00
|
|
|
|
/**
|
|
|
|
|
* Convert the GOTO block with condition at the end into a loop block and move the condition from the end to the
|
|
|
|
|
* start like wasm it required.
|
|
|
|
|
*
|
|
|
|
|
* @param gotoBlock
|
|
|
|
|
* the goto block
|
|
|
|
|
* @param conditionStart
|
|
|
|
|
* the code position where condition code start
|
|
|
|
|
* @param conditionEnd
|
|
|
|
|
* the end position
|
|
|
|
|
*/
|
|
|
|
|
private void convertToLoop( ParsedBlock gotoBlock, int conditionStart, int conditionEnd ) {
|
|
|
|
|
int conditionNew = gotoBlock.startPosition;
|
|
|
|
|
int nextPos = gotoBlock.nextPosition;
|
|
|
|
|
int conditionIdx = -1;
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
for( i = 0; i < instructions.size(); i++ ) {
|
|
|
|
|
WasmInstruction instr = instructions.get( i );
|
|
|
|
|
int codePos = instr.getCodePosition();
|
2021-11-16 21:28:11 +01:00
|
|
|
|
if( codePos == nextPos && conditionIdx < 0 ) {
|
2019-03-21 21:14:21 +01:00
|
|
|
|
conditionIdx = i;
|
|
|
|
|
}
|
|
|
|
|
if( codePos >= conditionEnd ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( codePos >= conditionStart ) {
|
|
|
|
|
instr.setCodePosition( conditionNew );
|
|
|
|
|
instructions.remove( i );
|
|
|
|
|
instructions.add( conditionIdx++, instr );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gotoBlock.op = JavaBlockOperator.LOOP;
|
2021-11-16 21:28:11 +01:00
|
|
|
|
gotoBlock.nextPosition = conditionStart; // Jump position for Continue
|
2019-03-21 21:14:21 +01:00
|
|
|
|
gotoBlock.endPosition = conditionEnd;
|
2019-03-31 11:23:45 +02:00
|
|
|
|
instructions.add( i, new WasmBlockInstruction( WasmBlockOperator.BR, 0, conditionNew, gotoBlock.lineNumber ) );
|
|
|
|
|
instructions.add( conditionIdx++, new WasmBlockInstruction( WasmBlockOperator.BR_IF, 1, conditionNew, gotoBlock.lineNumber ) );
|
2019-03-21 21:14:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-26 20:17:15 +01:00
|
|
|
|
/**
|
|
|
|
|
* Normalize all empty THEN blocks like:
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* if (condition) {
|
|
|
|
|
* } else {
|
|
|
|
|
* ....
|
|
|
|
|
* }
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* are changed to: if (!condition) { .... }
|
|
|
|
|
* </pre>
|
|
|
|
|
* The THEN block contains only a single GOTO operation. This operation is removed and the IF condition is negate.
|
|
|
|
|
* The removing of the GOTO operation make it easer to convert it to a valid WebAssembly structure without GOTO.
|
|
|
|
|
*
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the parsed operations
|
|
|
|
|
*/
|
|
|
|
|
private static void normalizeEmptyThenBlocks( List<ParsedBlock> parsedOperations ) {
|
|
|
|
|
// occur also with cascaded conditional operator like: int result = (a < 0 ? false : a == c ) && (b < 0 ? false : b == c ) ? 17 : 18;
|
|
|
|
|
for( int i = 0; i < parsedOperations.size() - 1; i++ ) {
|
|
|
|
|
ParsedBlock ifBlock = parsedOperations.get( i );
|
|
|
|
|
if( ifBlock.op == JavaBlockOperator.IF ) {
|
|
|
|
|
ParsedBlock nextBlock = parsedOperations.get( i + 1 );
|
|
|
|
|
if( nextBlock.op == JavaBlockOperator.GOTO && nextBlock.startPosition == ifBlock.nextPosition
|
|
|
|
|
&& ifBlock.endPosition == nextBlock.nextPosition ) {
|
|
|
|
|
((IfParsedBlock)ifBlock).negateCompare();
|
|
|
|
|
ifBlock.endPosition = nextBlock.endPosition;
|
|
|
|
|
parsedOperations.remove( i + 1 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-30 17:33:23 +02:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the branch tree for the given branch and parsed sub operations.
|
|
|
|
|
*
|
|
|
|
|
* @param parent the parent branch/block in the hierarchy.
|
|
|
|
|
* @param parsedOperations the not consumed parsed operations of the parent block.
|
|
|
|
|
*/
|
|
|
|
|
private void calculate( BranchNode parent, List<ParsedBlock> parsedOperations ) {
|
|
|
|
|
while( !parsedOperations.isEmpty() ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.remove( 0 );
|
|
|
|
|
switch( parsedBlock.op ) {
|
2018-03-27 20:04:35 +02:00
|
|
|
|
case IF:
|
2018-08-03 18:04:08 +02:00
|
|
|
|
calculateIf( parent, (IfParsedBlock)parsedBlock, parsedOperations );
|
2018-03-27 20:04:35 +02:00
|
|
|
|
break;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
case SWITCH:
|
2018-08-03 17:25:31 +02:00
|
|
|
|
calculateSwitch( parent, (SwitchParsedBlock)parsedBlock, parsedOperations );
|
2018-05-04 20:52:54 +02:00
|
|
|
|
break;
|
2018-05-20 11:54:06 +02:00
|
|
|
|
case GOTO:
|
2018-08-03 17:25:31 +02:00
|
|
|
|
calculateGoto( parent, parsedBlock, parsedOperations );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
break;
|
|
|
|
|
case LOOP:
|
2018-08-03 17:25:31 +02:00
|
|
|
|
calculateLoop( parent, parsedBlock, parsedOperations );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
break;
|
2018-11-03 18:01:42 +01:00
|
|
|
|
case TRY:
|
|
|
|
|
calculateTry( parent, (TryCatchParsedBlock)parsedBlock, parsedOperations );
|
|
|
|
|
break;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
default:
|
2020-04-10 22:22:45 +02:00
|
|
|
|
throw new WasmException( "Unimplemented block code operation: " + parsedBlock.op, parsedBlock.lineNumber );
|
2018-03-27 20:04:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-16 21:28:11 +01:00
|
|
|
|
/**
|
|
|
|
|
* Add a break to the node if the block jump to the continue position of an outer loop.
|
|
|
|
|
*
|
|
|
|
|
* @param parent
|
|
|
|
|
* the container for adding the break
|
|
|
|
|
* @param startBlock
|
|
|
|
|
* an IF or GOTO block.
|
|
|
|
|
* @return true, if the break was added
|
|
|
|
|
*/
|
|
|
|
|
private boolean addBreakIfLoopContinue( BranchNode parent, ParsedBlock startBlock ) {
|
|
|
|
|
BranchNode main = parent;
|
|
|
|
|
int endPos = startBlock.endPosition;
|
|
|
|
|
int breakDeep = 0;
|
|
|
|
|
while( main != null ) {
|
|
|
|
|
if( main.startOp == WasmBlockOperator.LOOP && main.continuePos == endPos ) {
|
|
|
|
|
// possible operation values here are IF and GOTO
|
|
|
|
|
WasmBlockOperator op = startBlock.op == JavaBlockOperator.IF ? WasmBlockOperator.BR_IF : WasmBlockOperator.BR;
|
|
|
|
|
int startPos = startBlock.nextPosition;
|
|
|
|
|
parent.add( new BranchNode( startPos, startPos, op, null, breakDeep ) );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
main = main.parent;
|
|
|
|
|
breakDeep++;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 20:04:35 +02:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the ELSE and END position of an IF control structure.
|
|
|
|
|
*
|
2018-03-30 17:33:23 +02:00
|
|
|
|
* @param parent
|
|
|
|
|
* the parent branch
|
2018-03-27 20:04:35 +02:00
|
|
|
|
* @param startBlock
|
|
|
|
|
* the start block of the if control structure
|
2018-03-30 17:33:23 +02:00
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
2018-03-27 20:04:35 +02:00
|
|
|
|
*/
|
2018-08-03 18:04:08 +02:00
|
|
|
|
private void calculateIf( BranchNode parent, IfParsedBlock startBlock, List<ParsedBlock> parsedOperations ) {
|
2020-03-26 18:21:50 +01:00
|
|
|
|
instructions.remove( startBlock.jump );
|
2019-08-02 22:39:04 +02:00
|
|
|
|
IfPositions positions = searchElsePosition( startBlock, parsedOperations );
|
|
|
|
|
|
2021-11-16 21:28:11 +01:00
|
|
|
|
if( addBreakIfLoopContinue( parent, startBlock ) ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-02 22:39:04 +02:00
|
|
|
|
BranchNode main = parent;
|
|
|
|
|
for( int i = 0; i < positions.ifCount; i++ ) {
|
|
|
|
|
IfParsedBlock parsedBlock = (IfParsedBlock)parsedOperations.remove( 0 );
|
2020-03-26 18:21:50 +01:00
|
|
|
|
instructions.remove( parsedBlock.jump );
|
2019-08-02 22:39:04 +02:00
|
|
|
|
if( startBlock.endPosition < positions.thenPos || startBlock.endPosition == positions.elsePos ) {
|
|
|
|
|
// AND concatenation (&& operator)
|
|
|
|
|
int pos = parsedBlock.startPosition + 1;
|
|
|
|
|
main.add( new BranchNode( startBlock.nextPosition, pos, WasmBlockOperator.IF, null ) );
|
|
|
|
|
main.add( new BranchNode( pos, pos + 1, WasmBlockOperator.ELSE, WasmBlockOperator.END ) );
|
|
|
|
|
startBlock.negateCompare();
|
|
|
|
|
insertConstBeforePosition( 0, pos + 1, parsedBlock.lineNumber ); // 0 --> FALSE for the next if expression
|
|
|
|
|
} else {
|
|
|
|
|
// OR concatenation (|| operator)
|
|
|
|
|
int pos = startBlock.startPosition + 2; // startBlock.startPosition is the position of the compare operation which need before this if construct
|
|
|
|
|
main.add( new BranchNode( pos, startBlock.nextPosition, WasmBlockOperator.IF, null ) );
|
2022-01-01 21:03:06 +01:00
|
|
|
|
BranchNode node = new BranchNode( startBlock.nextPosition, positions.thenPos - 1, WasmBlockOperator.ELSE, WasmBlockOperator.END );
|
2019-08-02 22:39:04 +02:00
|
|
|
|
main.add( node );
|
|
|
|
|
main = node;
|
|
|
|
|
insertConstBeforePosition( 1, pos + 1, startBlock.lineNumber ); // 1 --> TRUE for the next if expression
|
|
|
|
|
}
|
|
|
|
|
startBlock = parsedBlock;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-30 17:33:23 +02:00
|
|
|
|
int i = 0;
|
|
|
|
|
int endPos = Math.min( startBlock.endPosition, parent.endPos );
|
2019-03-19 19:35:42 +01:00
|
|
|
|
int startPos = startBlock.nextPosition;
|
2018-06-02 23:57:04 +02:00
|
|
|
|
if( startPos > endPos ) {
|
|
|
|
|
// the condition in a do while(condition) loop
|
2020-03-21 21:16:10 +01:00
|
|
|
|
int breakDeep = calculateContinueDeep( parent, endPos );
|
2020-04-11 17:04:21 +02:00
|
|
|
|
instructions.add( findIdxOfCodePos( startPos ), new WasmBlockInstruction( WasmBlockOperator.BR_IF, breakDeep, startPos - 1, startBlock.lineNumber ) );
|
2018-06-02 23:57:04 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-03-21 21:16:10 +01:00
|
|
|
|
|
2018-03-30 17:33:23 +02:00
|
|
|
|
BranchNode branch = null;
|
|
|
|
|
for( ; i < parsedOperations.size(); i++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( i );
|
2019-08-02 11:11:10 +02:00
|
|
|
|
if( parsedBlock.nextPosition == positions.elsePos && parsedBlock.op == JavaBlockOperator.GOTO && parsedBlock.startPosition < parsedBlock.endPosition ) {
|
2018-03-30 17:33:23 +02:00
|
|
|
|
parsedOperations.remove( i );
|
2019-03-20 19:11:38 +01:00
|
|
|
|
// end position can not be outside of the parent
|
|
|
|
|
endPos = Math.min( parsedBlock.endPosition, parent.endPos );
|
|
|
|
|
|
2019-08-02 11:11:10 +02:00
|
|
|
|
branch = new BranchNode( startPos, positions.elsePos, WasmBlockOperator.IF, null );
|
2018-03-30 17:33:23 +02:00
|
|
|
|
parent.add( branch );
|
|
|
|
|
if( i > 0 ) {
|
|
|
|
|
calculate( branch, parsedOperations.subList( 0, i ) );
|
2019-03-20 19:11:38 +01:00
|
|
|
|
i = 0;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2018-08-02 12:18:31 +02:00
|
|
|
|
|
2021-11-16 21:28:11 +01:00
|
|
|
|
if( addBreakIfLoopContinue( branch, parsedBlock ) ) {
|
|
|
|
|
branch.endOp = WasmBlockOperator.END;
|
|
|
|
|
endPos = branch.endPos;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-08-10 20:35:17 +02:00
|
|
|
|
// if with block type signature must have an else block
|
2019-07-23 18:23:59 +02:00
|
|
|
|
int breakDeep = calculateBreakDeep( parent, endPos );
|
|
|
|
|
if( breakDeep > 0 ) {
|
2018-08-02 12:18:31 +02:00
|
|
|
|
branch.endOp = WasmBlockOperator.END;
|
2019-08-02 11:11:10 +02:00
|
|
|
|
branch.add( new BranchNode( positions.elsePos, endPos, WasmBlockOperator.BR, null, breakDeep + 1 ) );
|
2018-08-02 12:18:31 +02:00
|
|
|
|
endPos = branch.endPos;
|
|
|
|
|
} else {
|
2021-10-24 15:29:12 +02:00
|
|
|
|
branch.elseEndPos = endPos;
|
2019-08-02 11:11:10 +02:00
|
|
|
|
branch = new BranchNode( positions.elsePos, endPos, WasmBlockOperator.ELSE, WasmBlockOperator.END );
|
2018-08-02 12:18:31 +02:00
|
|
|
|
parent.add( branch );
|
|
|
|
|
}
|
2018-03-27 20:04:35 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-09-25 22:32:41 +02:00
|
|
|
|
if( parsedBlock.nextPosition >= positions.elsePos ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-03-27 20:04:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-30 17:33:23 +02:00
|
|
|
|
if( branch == null ) {
|
2022-01-01 21:03:06 +01:00
|
|
|
|
branch = new BranchNode( startPos - 1, endPos, WasmBlockOperator.IF, WasmBlockOperator.END, ValueType.empty );
|
2018-03-30 17:33:23 +02:00
|
|
|
|
parent.add( branch );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
if( startBlock.endPosition > parent.endPos ) {
|
|
|
|
|
int count = 0;
|
|
|
|
|
for( int j = 0; j < instructions.size(); j++ ) {
|
|
|
|
|
WasmInstruction instr = instructions.get( j );
|
|
|
|
|
int pos = instr.getCodePosition();
|
|
|
|
|
if( pos < startPos ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if( pos >= endPos ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( instr.getType() == Type.Jump ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
if( count == 0 ) {
|
2022-01-01 21:03:06 +01:00
|
|
|
|
// we jump outside the parent and there are no instructions. This is like a conditional break
|
2021-10-24 15:29:12 +02:00
|
|
|
|
//TODO should be BR_IF
|
2022-01-01 21:03:06 +01:00
|
|
|
|
breakOperations.add( new BreakBlock( branch, endPos, startBlock.endPosition ) );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2018-08-03 18:04:08 +02:00
|
|
|
|
startBlock.negateCompare();
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2018-03-27 20:04:35 +02:00
|
|
|
|
/**
|
|
|
|
|
* Search the index in the stack to add the END operator
|
|
|
|
|
*/
|
2018-03-30 17:33:23 +02:00
|
|
|
|
for( ; i < parsedOperations.size(); i++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( i );
|
|
|
|
|
if( parsedBlock.startPosition >= endPos ) {
|
2018-03-27 20:04:35 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
if( i > 0 ) {
|
|
|
|
|
calculate( branch, parsedOperations.subList( 0, i ) );
|
|
|
|
|
}
|
2018-03-25 12:57:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 19:15:47 +02:00
|
|
|
|
/**
|
2019-08-02 11:11:10 +02:00
|
|
|
|
* Search the start positions of the THEN and ELSE branch from an IF control structure.
|
2019-07-29 19:15:47 +02:00
|
|
|
|
*
|
|
|
|
|
* @param startBlock
|
2019-08-02 11:11:10 +02:00
|
|
|
|
* the start block of the IF control structure
|
2019-07-29 19:15:47 +02:00
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
2019-08-02 11:11:10 +02:00
|
|
|
|
* @return the calculated positions
|
2019-07-29 19:15:47 +02:00
|
|
|
|
*/
|
2019-08-02 11:11:10 +02:00
|
|
|
|
private IfPositions searchElsePosition( IfParsedBlock startBlock, List<ParsedBlock> parsedOperations ) {
|
2021-05-16 18:44:23 +02:00
|
|
|
|
int parsedOpCount = parsedOperations.size();
|
|
|
|
|
|
2020-05-25 19:28:47 +02:00
|
|
|
|
// find the end position of the else block
|
|
|
|
|
int endElse = startBlock.endPosition;
|
2021-05-16 18:44:23 +02:00
|
|
|
|
boolean newElsePositionFound = true;
|
|
|
|
|
LOOP:
|
2020-05-17 13:57:30 +02:00
|
|
|
|
for( int i = 0; i < parsedOpCount; i++ ) {
|
2021-05-16 18:44:23 +02:00
|
|
|
|
ParsedBlock parsedBlock;
|
|
|
|
|
if( newElsePositionFound ) {
|
|
|
|
|
newElsePositionFound = false;
|
|
|
|
|
for( int j = i; j < parsedOpCount; j++ ) {
|
|
|
|
|
parsedBlock = parsedOperations.get( j );
|
|
|
|
|
if( parsedBlock.op == JavaBlockOperator.GOTO ) {
|
|
|
|
|
if( parsedBlock.nextPosition == endElse ) {
|
|
|
|
|
break LOOP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
parsedBlock = parsedOperations.get( i );
|
2020-05-25 19:28:47 +02:00
|
|
|
|
switch( parsedBlock.op ) {
|
|
|
|
|
case IF:
|
|
|
|
|
case GOTO:
|
2021-05-16 18:44:23 +02:00
|
|
|
|
if( parsedBlock.startPosition < endElse && endElse < parsedBlock.endPosition ) {
|
|
|
|
|
endElse = parsedBlock.endPosition;
|
|
|
|
|
newElsePositionFound = true;
|
2020-05-25 19:28:47 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( parsedBlock.startPosition > endElse ) {
|
|
|
|
|
parsedOpCount = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-17 13:57:30 +02:00
|
|
|
|
|
2020-05-25 19:28:47 +02:00
|
|
|
|
// first search for the first GOTO, any IF that point after the GOTO can not be part of the primary IF condition.
|
|
|
|
|
// This occur with a second IF inside of the THEN. This can jump directly to the end of the ELSE.
|
|
|
|
|
for( int i = 0; i < parsedOpCount; i++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( i );
|
|
|
|
|
if( parsedBlock.op == JavaBlockOperator.GOTO ) {
|
2020-05-17 13:57:30 +02:00
|
|
|
|
// find the last IF that point to this GOTO
|
|
|
|
|
int gotoNext = parsedBlock.nextPosition;
|
|
|
|
|
for( ; i > 0; i-- ) {
|
|
|
|
|
parsedBlock = parsedOperations.get( i-1 );
|
|
|
|
|
if( parsedBlock.endPosition == gotoNext ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
parsedOpCount = i;
|
2019-07-29 19:15:47 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-02 11:11:10 +02:00
|
|
|
|
|
|
|
|
|
int ifCount = 0;
|
|
|
|
|
int thenPos = startBlock.nextPosition;
|
|
|
|
|
int elsePos = startBlock.endPosition;
|
2020-05-17 13:57:30 +02:00
|
|
|
|
for( ; ifCount < parsedOpCount; ifCount++ ) {
|
2019-08-02 11:11:10 +02:00
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( ifCount );
|
2020-05-27 21:21:50 +02:00
|
|
|
|
if( parsedBlock.op != JavaBlockOperator.IF || parsedBlock.endPosition > endElse ) {
|
2019-08-02 11:11:10 +02:00
|
|
|
|
// seems a second IF inside the THEN part.
|
2019-07-29 19:15:47 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-05-27 21:21:50 +02:00
|
|
|
|
if( parsedBlock.endPosition < elsePos ) {
|
|
|
|
|
// The IF jumps not to ELSE part. This can be an inner IF or it is a combination of (||) and (&&) operation
|
|
|
|
|
boolean isContinue = false;
|
|
|
|
|
for( int i = ifCount + 1; i < parsedOpCount; i++ ) {
|
|
|
|
|
ParsedBlock op = parsedOperations.get( i );
|
2022-02-13 15:46:43 +01:00
|
|
|
|
if( op.endPosition >= elsePos ) {
|
|
|
|
|
isContinue = op.op == JavaBlockOperator.IF;
|
2020-05-27 21:21:50 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if( !isContinue ) {
|
|
|
|
|
// really seems a second IF within the THEN part.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-02 11:11:10 +02:00
|
|
|
|
if( parsedBlock.nextPosition > elsePos ) {
|
|
|
|
|
// occur if there are 2 IF blocks without ELSE behind the other, this IF is the second that we can cancel the analyze at this point
|
|
|
|
|
break;
|
2019-07-29 19:15:47 +02:00
|
|
|
|
}
|
2019-08-02 11:11:10 +02:00
|
|
|
|
thenPos = Math.max( thenPos, parsedBlock.nextPosition );
|
|
|
|
|
elsePos = Math.max( elsePos, parsedBlock.endPosition );
|
2019-07-29 19:15:47 +02:00
|
|
|
|
}
|
2019-08-02 11:11:10 +02:00
|
|
|
|
IfPositions pos = new IfPositions();
|
|
|
|
|
pos.ifCount = ifCount;
|
|
|
|
|
pos.thenPos = thenPos;
|
|
|
|
|
pos.elsePos = elsePos;
|
|
|
|
|
return pos;
|
2019-07-29 19:15:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert a constant i32 operation before the given code position
|
|
|
|
|
*
|
|
|
|
|
* @param constant
|
|
|
|
|
* the i32 value
|
|
|
|
|
* @param pos
|
|
|
|
|
* the code position
|
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the line number for possible error messages
|
|
|
|
|
*/
|
|
|
|
|
private void insertConstBeforePosition( int constant, int pos, int lineNumber ) {
|
2020-04-11 17:04:21 +02:00
|
|
|
|
instructions.add( findIdxOfCodePos( pos ), new WasmConstInstruction( constant, pos - 1, lineNumber ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find the index of the instruction with the given code position.
|
|
|
|
|
*
|
|
|
|
|
* @param codePosition
|
|
|
|
|
* the java position
|
|
|
|
|
* @return the index
|
|
|
|
|
*/
|
|
|
|
|
private int findIdxOfCodePos( int codePosition ) {
|
|
|
|
|
int size = instructions.size();
|
|
|
|
|
for( int i = 0; i < size; i++ ) {
|
|
|
|
|
if( instructions.get( i ).getCodePosition() >= codePosition ) {
|
|
|
|
|
return i;
|
2019-07-29 19:15:47 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-11 17:04:21 +02:00
|
|
|
|
return size;
|
2019-07-29 19:15:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 21:16:10 +01:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the break deep for a continue in a do while(condition) loop.
|
|
|
|
|
*
|
|
|
|
|
* @param parent
|
|
|
|
|
* current branch
|
|
|
|
|
* @param startPos
|
|
|
|
|
* the start position of the loop
|
|
|
|
|
* @return the deep count
|
|
|
|
|
*/
|
|
|
|
|
private int calculateContinueDeep( BranchNode parent, int startPos ) {
|
|
|
|
|
int deep = 0;
|
|
|
|
|
while( parent != null && parent.startPos > startPos ) {
|
|
|
|
|
deep++;
|
|
|
|
|
parent = parent.parent;
|
|
|
|
|
}
|
|
|
|
|
return deep;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-02 12:18:31 +02:00
|
|
|
|
/**
|
2020-03-21 15:58:50 +01:00
|
|
|
|
* Calculate the deep of a break. A GOTO or IF in Java can jump out multiple loops. We need to calculate how many levels we need to jump.
|
2018-08-02 12:18:31 +02:00
|
|
|
|
*
|
|
|
|
|
* @param parent
|
|
|
|
|
* the current parent node.
|
|
|
|
|
* @param endPos
|
|
|
|
|
* the end position of the jump
|
|
|
|
|
* @return the deep level for "br" or -1 if there is no parent node with this end position
|
|
|
|
|
*/
|
|
|
|
|
private int calculateBreakDeep( BranchNode parent, int endPos ) {
|
|
|
|
|
int deep = -1;
|
2020-03-21 15:58:50 +01:00
|
|
|
|
boolean wasLoop = false;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
while( parent != null && parent.elseEndPos <= endPos && parent.data == null ) {
|
2018-08-02 12:18:31 +02:00
|
|
|
|
deep++;
|
2020-03-21 15:58:50 +01:00
|
|
|
|
wasLoop |= parent.startOp == WasmBlockOperator.LOOP; // only in a loop we need to jump for exit
|
2018-08-02 12:18:31 +02:00
|
|
|
|
parent = parent.parent;
|
|
|
|
|
}
|
2020-03-21 15:58:50 +01:00
|
|
|
|
return wasLoop ? deep : -1;
|
2018-08-02 12:18:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 20:52:54 +02:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the blocks of a switch.
|
|
|
|
|
*
|
|
|
|
|
* Sample: The follow Java code:
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* int b;
|
|
|
|
|
* switch( a ) {
|
|
|
|
|
* case 8:
|
|
|
|
|
* b = 1;
|
|
|
|
|
* break;
|
|
|
|
|
* default:
|
|
|
|
|
* b = 9;
|
|
|
|
|
* }
|
|
|
|
|
* return b;
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
2018-05-11 22:07:22 +02:00
|
|
|
|
* Should be converted to the follow Wasm code for tableswitch:
|
2018-05-04 20:52:54 +02:00
|
|
|
|
*
|
|
|
|
|
* <pre>
|
2018-05-11 22:07:22 +02:00
|
|
|
|
* block
|
|
|
|
|
* block
|
|
|
|
|
* block
|
|
|
|
|
* get_local 0
|
|
|
|
|
* i32.const 8
|
|
|
|
|
* i32.sub
|
|
|
|
|
* br_table 0 1
|
|
|
|
|
* end
|
|
|
|
|
* i32.const 1
|
|
|
|
|
* set_local 1
|
|
|
|
|
* br 1
|
|
|
|
|
* end
|
|
|
|
|
* i32.const 9
|
|
|
|
|
* set_local 1
|
|
|
|
|
* end
|
|
|
|
|
* get_local 1
|
|
|
|
|
* return
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* and for lookupswitch
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* block
|
|
|
|
|
* block
|
|
|
|
|
* block
|
|
|
|
|
* get_local 0
|
|
|
|
|
* tee_local $switch
|
|
|
|
|
* i32.const 8
|
|
|
|
|
* i32.eq
|
|
|
|
|
* br_if 0
|
|
|
|
|
* br 1
|
|
|
|
|
* end
|
|
|
|
|
* i32.const 1
|
|
|
|
|
* set_local 1
|
|
|
|
|
* br 1
|
|
|
|
|
* end
|
|
|
|
|
* i32.const 9
|
|
|
|
|
* set_local 1
|
|
|
|
|
* end
|
|
|
|
|
* get_local 1
|
|
|
|
|
* return
|
2018-05-04 20:52:54 +02:00
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* @param parent
|
|
|
|
|
* the parent branch
|
|
|
|
|
* @param switchBlock
|
|
|
|
|
* the start block of the if control structure
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
|
|
|
|
*/
|
2018-08-03 17:25:31 +02:00
|
|
|
|
private void calculateSwitch( BranchNode parent, SwitchParsedBlock switchBlock, List<ParsedBlock> parsedOperations ) {
|
2018-05-07 18:46:15 +02:00
|
|
|
|
int startPosition = ((ParsedBlock)switchBlock).startPosition;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
int posCount = switchBlock.positions.length;
|
2018-05-12 09:59:42 +02:00
|
|
|
|
boolean isTable = switchBlock.keys == null;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
|
2018-05-11 22:07:22 +02:00
|
|
|
|
// create a helper structure
|
|
|
|
|
SwitchCase[] cases = new SwitchCase[posCount + 1];
|
|
|
|
|
SwitchCase switchCase = cases[posCount] = new SwitchCase();
|
|
|
|
|
switchCase.key = Long.MAX_VALUE;
|
|
|
|
|
switchCase.position = switchBlock.defaultPosition;
|
|
|
|
|
for( int i = 0; i < switchBlock.positions.length; i++ ) {
|
|
|
|
|
switchCase = cases[i] = new SwitchCase();
|
|
|
|
|
switchCase.key = isTable ? i : switchBlock.keys[i];
|
|
|
|
|
switchCase.position = switchBlock.positions[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculate the block number for ever switch case depending its position order
|
|
|
|
|
Arrays.sort( cases, ( a, b ) -> Integer.compare( a.position, b.position ) );
|
2018-06-01 22:04:34 +02:00
|
|
|
|
int blockCount = -1;
|
2018-05-11 22:07:22 +02:00
|
|
|
|
int lastPosition = -1;
|
|
|
|
|
BranchNode brTableNode = null;
|
|
|
|
|
BranchNode blockNode = null;
|
|
|
|
|
for( int i = 0; i < cases.length; i++ ) {
|
|
|
|
|
switchCase = cases[i];
|
|
|
|
|
int currentPosition = switchCase.position;
|
|
|
|
|
if( lastPosition != currentPosition ) {
|
|
|
|
|
if( isTable && blockNode == null ) {
|
|
|
|
|
blockNode = brTableNode = new BranchNode( currentPosition, currentPosition, WasmBlockOperator.BR_TABLE, null );
|
|
|
|
|
}
|
|
|
|
|
lastPosition = currentPosition;
|
|
|
|
|
blockCount++;
|
|
|
|
|
BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
|
|
|
|
if( blockNode != null ) {
|
2018-05-05 21:43:30 +02:00
|
|
|
|
node.add( blockNode );
|
|
|
|
|
}
|
2018-05-11 22:07:22 +02:00
|
|
|
|
blockNode = node;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
}
|
2018-06-01 22:04:34 +02:00
|
|
|
|
switchCase.block = blockCount;
|
2018-05-11 22:07:22 +02:00
|
|
|
|
}
|
2018-05-05 21:43:30 +02:00
|
|
|
|
|
2020-03-05 21:31:15 +01:00
|
|
|
|
// add extra blocks for forward GOTO jumps like in SWITCH of Strings
|
|
|
|
|
for( int p = 0; p < parsedOperations.size(); p++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( p );
|
|
|
|
|
int start = parsedBlock.startPosition;
|
|
|
|
|
if( startPosition >= lastPosition ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( parsedBlock.op != JavaBlockOperator.IF ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
int end = parsedBlock.endPosition;
|
|
|
|
|
if( start < end ) {
|
|
|
|
|
BranchNode branch = blockNode;
|
2020-03-07 17:01:59 +01:00
|
|
|
|
while( branch.size() > 0 ) {
|
2020-03-05 21:31:15 +01:00
|
|
|
|
BranchNode node = branch.get( 0 );
|
|
|
|
|
if( start > node.endPos ) {
|
|
|
|
|
if( end > branch.endPos ) {
|
|
|
|
|
BranchNode parentNode = branch;
|
2020-03-05 22:30:36 +01:00
|
|
|
|
while( parentNode != null && end > parentNode.endPos ) {
|
2020-03-05 21:31:15 +01:00
|
|
|
|
parentNode = parentNode.parent;
|
|
|
|
|
}
|
2020-03-05 22:30:36 +01:00
|
|
|
|
BranchNode middleNode = new BranchNode( startPosition, end, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
|
|
|
|
if( parentNode != null ) {
|
|
|
|
|
BranchNode child = parentNode.remove( 0 );
|
|
|
|
|
parentNode.add( middleNode );
|
|
|
|
|
middleNode.add( child );
|
2020-03-07 17:01:59 +01:00
|
|
|
|
patchBrDeep( middleNode );
|
2020-03-05 22:30:36 +01:00
|
|
|
|
} else {
|
|
|
|
|
// occur if the default is not the last case in the switch
|
|
|
|
|
middleNode.add( blockNode );
|
|
|
|
|
blockNode = middleNode;
|
|
|
|
|
lastPosition = end;
|
|
|
|
|
}
|
2020-03-05 21:31:15 +01:00
|
|
|
|
}
|
|
|
|
|
break;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
}
|
2020-03-05 21:31:15 +01:00
|
|
|
|
branch = node;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-05 21:31:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handle the GOTO (breaks) at the end of the CASE blocks.
|
|
|
|
|
for( Iterator<ParsedBlock> it = parsedOperations.iterator(); it.hasNext(); ) {
|
|
|
|
|
ParsedBlock parsedBlock = it.next();
|
|
|
|
|
int start = parsedBlock.startPosition;
|
|
|
|
|
if( startPosition >= lastPosition ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch( parsedBlock.op ) {
|
|
|
|
|
case GOTO:
|
|
|
|
|
case IF:
|
|
|
|
|
int end = parsedBlock.endPosition;
|
|
|
|
|
if( start < end ) {
|
|
|
|
|
BranchNode branch = blockNode;
|
|
|
|
|
while( branch.size() > 0 ) {
|
|
|
|
|
BranchNode node = branch.get( 0 );
|
|
|
|
|
if( start > node.endPos ) {
|
|
|
|
|
if( end >= branch.endPos ) {
|
|
|
|
|
blockCount = 0;
|
|
|
|
|
BranchNode parentNode = branch;
|
|
|
|
|
while( parentNode != null && end > parentNode.endPos ) {
|
|
|
|
|
parentNode = parentNode.parent;
|
|
|
|
|
blockCount++;
|
|
|
|
|
}
|
|
|
|
|
WasmBlockOperator startOp;
|
|
|
|
|
if( parsedBlock.op == JavaBlockOperator.GOTO ) {
|
|
|
|
|
startOp = WasmBlockOperator.BR;
|
2020-03-05 22:30:36 +01:00
|
|
|
|
lastPosition = Math.max( lastPosition, end );
|
2020-03-05 21:31:15 +01:00
|
|
|
|
} else {
|
|
|
|
|
startOp = WasmBlockOperator.BR_IF;
|
2021-11-20 21:45:59 +01:00
|
|
|
|
instructions.remove( ((IfParsedBlock)parsedBlock).jump );
|
2020-03-05 21:31:15 +01:00
|
|
|
|
}
|
|
|
|
|
start++;
|
|
|
|
|
branch.add( new BranchNode( start, start, startOp, null, blockCount ) );
|
|
|
|
|
it.remove();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
branch = node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-11 22:07:22 +02:00
|
|
|
|
}
|
2018-05-05 21:43:30 +02:00
|
|
|
|
|
2018-05-11 22:07:22 +02:00
|
|
|
|
// Create the main block around the switch
|
|
|
|
|
BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
|
|
|
|
switchNode.add( blockNode );
|
|
|
|
|
parent.add( switchNode );
|
2018-05-05 21:43:30 +02:00
|
|
|
|
|
2018-05-11 22:07:22 +02:00
|
|
|
|
if( brTableNode != null ) {
|
2020-03-07 17:01:59 +01:00
|
|
|
|
// sort back in the natural order and create a br_table
|
|
|
|
|
Arrays.sort( cases, ( a, b ) -> Long.compare( a.key, b.key ) );
|
|
|
|
|
int[] data = new int[cases.length];
|
|
|
|
|
for( int i = 0; i < data.length; i++ ) {
|
|
|
|
|
data[i] = cases[i].block;
|
|
|
|
|
}
|
2018-05-05 21:43:30 +02:00
|
|
|
|
brTableNode.data = data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 17:01:59 +01:00
|
|
|
|
/**
|
|
|
|
|
* Patch the existing BR instructions after a new BLOCK node was injected in the hierarchy. The break position must
|
|
|
|
|
* only increment if the old break position is outside of the new BLOCK.
|
|
|
|
|
*
|
|
|
|
|
* @param newNode
|
|
|
|
|
* the new node
|
|
|
|
|
*/
|
|
|
|
|
private void patchBrDeep( BranchNode newNode ) {
|
|
|
|
|
for( WasmInstruction instr : instructions ) {
|
|
|
|
|
int codePos = instr.getCodePosition();
|
|
|
|
|
if( codePos < newNode.startPos ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if( codePos >= newNode.endPos ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( instr.getType() != Type.Block ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
WasmBlockInstruction blockInstr = (WasmBlockInstruction)instr;
|
|
|
|
|
if( blockInstr.getOperation() != WasmBlockOperator.BR ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Integer data = (Integer)blockInstr.getData();
|
|
|
|
|
int blockCount = 0;
|
|
|
|
|
while( newNode.size() > 0 && newNode.endPos > codePos ) {
|
|
|
|
|
newNode = newNode.get( 0 );
|
|
|
|
|
blockCount++;
|
|
|
|
|
}
|
|
|
|
|
if( blockCount <= data ) { // old break position was outside of the new block
|
|
|
|
|
blockInstr.setData( data + 1 );
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-16 21:28:11 +01:00
|
|
|
|
patchBrDeepInTree( newNode, 0 );
|
2022-01-02 18:17:55 +01:00
|
|
|
|
|
|
|
|
|
// also change the parent for all break operations
|
|
|
|
|
BranchNode parent = newNode.parent;
|
|
|
|
|
for( BreakBlock breakBlock : breakOperations ) {
|
|
|
|
|
if( breakBlock.branch == parent ) {
|
|
|
|
|
int breakPos = breakBlock.breakPos;
|
|
|
|
|
if( breakPos >= newNode.startPos && breakPos < newNode.endPos ) {
|
|
|
|
|
breakBlock.branch = newNode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-16 21:28:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Patch the existing BR instructions in the tree after a new BLOCK node was injected in the hierarchy. The break position must
|
|
|
|
|
* only increment if the old break position is outside of the new BLOCK.
|
|
|
|
|
*
|
|
|
|
|
* @param newNode
|
|
|
|
|
* the new node
|
|
|
|
|
* @param deep
|
|
|
|
|
* the deep of recursion
|
|
|
|
|
*/
|
|
|
|
|
private void patchBrDeepInTree( BranchNode newNode, int deep ) {
|
|
|
|
|
for( BranchNode node : newNode ) {
|
|
|
|
|
if( node.startOp == WasmBlockOperator.BR || node.startOp == WasmBlockOperator.BR_IF ) {
|
|
|
|
|
Integer data = (Integer)node.data;
|
|
|
|
|
if( data >= deep ) {
|
|
|
|
|
node.data = data + 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
patchBrDeepInTree( node, deep + 1 );
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-07 17:01:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-05 21:43:30 +02:00
|
|
|
|
/**
|
|
|
|
|
* Helper structure for caculateSwitch
|
|
|
|
|
*/
|
|
|
|
|
private static class SwitchCase {
|
2018-05-11 22:07:22 +02:00
|
|
|
|
long key;
|
2018-05-05 21:43:30 +02:00
|
|
|
|
int position;
|
|
|
|
|
int block;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 11:54:06 +02:00
|
|
|
|
/**
|
|
|
|
|
* The not consumed GOTO operators of IF THEN ELSE must be break or continue in a loop.
|
|
|
|
|
*
|
|
|
|
|
* @param parent
|
|
|
|
|
* the parent branch
|
|
|
|
|
* @param gotoBlock
|
|
|
|
|
* the GOTO operation
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
|
|
|
|
*/
|
2018-08-03 17:25:31 +02:00
|
|
|
|
private void calculateGoto ( BranchNode parent, ParsedBlock gotoBlock, List<ParsedBlock> parsedOperations ) {
|
2020-01-05 20:32:26 +01:00
|
|
|
|
int jump = gotoBlock.endPosition; // jump position of the GOTO
|
|
|
|
|
int start = gotoBlock.startPosition;
|
|
|
|
|
if( start > jump ) {
|
|
|
|
|
// back jump to a previous position like in loops
|
2018-05-20 11:54:06 +02:00
|
|
|
|
int deep = 0;
|
|
|
|
|
while( parent != null ) {
|
2020-01-05 20:32:26 +01:00
|
|
|
|
if( parent.startOp == WasmBlockOperator.LOOP && parent.startPos == jump ) {
|
2020-05-03 21:42:10 +02:00
|
|
|
|
// the loop instruction itself doesn’t result in an iteration, instead a br 0 instruction causes the loop to repeat
|
2020-01-05 20:32:26 +01:00
|
|
|
|
parent.add( new BranchNode( start, start, WasmBlockOperator.BR, null, deep ) ); // continue to the start of the loop
|
2018-05-20 11:54:06 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
parent = parent.parent;
|
|
|
|
|
deep++;
|
|
|
|
|
}
|
2020-01-05 20:32:26 +01:00
|
|
|
|
} else {
|
|
|
|
|
if( gotoBlock.nextPosition == jump ) {
|
|
|
|
|
//A GOTO to the next position is like a NOP and can be ignored.
|
|
|
|
|
//Occur in java.lang.Integer.getChars(int, int, char[]) of Java 8
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
2022-01-02 18:17:55 +01:00
|
|
|
|
breakOperations.add( new BreakBlock( parent, start, jump ) );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate the needed nodes for a loop.
|
|
|
|
|
* @param parent
|
|
|
|
|
* the parent branch
|
|
|
|
|
* @param loopBlock
|
|
|
|
|
* the virtual LOOP operation
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
|
|
|
|
*/
|
2018-08-03 17:25:31 +02:00
|
|
|
|
private void calculateLoop ( BranchNode parent, ParsedBlock loopBlock, List<ParsedBlock> parsedOperations ) {
|
2018-08-02 12:18:31 +02:00
|
|
|
|
BranchNode blockNode = new BranchNode( loopBlock.startPosition, loopBlock.endPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
|
|
|
|
parent.add( blockNode );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
BranchNode loopNode = new BranchNode( loopBlock.startPosition, loopBlock.endPosition, WasmBlockOperator.LOOP, WasmBlockOperator.END );
|
2021-11-16 21:28:11 +01:00
|
|
|
|
loopNode.continuePos = loopBlock.nextPosition;
|
2018-08-02 12:18:31 +02:00
|
|
|
|
blockNode.add( loopNode );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
|
for( ; idx < parsedOperations.size(); idx++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( idx );
|
|
|
|
|
if( parsedBlock.startPosition >= loopBlock.endPosition ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
calculate( loopNode, parsedOperations.subList( 0, idx ) );
|
|
|
|
|
// add the "br 0" as last child to receive the right order
|
2018-05-20 15:57:38 +02:00
|
|
|
|
// loopNode.add( new BranchNode( loopBlock.endPosition, loopBlock.endPosition, WasmBlockOperator.BR, null, 0 ) ); // continue to the start of the loop
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 18:01:42 +01:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the needed nodes for try/catch
|
2019-03-02 21:54:27 +01:00
|
|
|
|
* Sample: The follow Java code:
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* try {
|
|
|
|
|
* code1
|
|
|
|
|
* catch(Exception ex)
|
|
|
|
|
* code2
|
|
|
|
|
* }
|
|
|
|
|
* code3
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
2020-04-10 16:30:59 +02:00
|
|
|
|
* Should be converted to the follow Wasm code for try/catch:
|
2019-03-02 21:54:27 +01:00
|
|
|
|
*
|
|
|
|
|
* <pre>
|
2020-04-18 13:09:11 +02:00
|
|
|
|
* try
|
|
|
|
|
* code1
|
|
|
|
|
* catch
|
|
|
|
|
* block (param exnref)(result anyref)
|
|
|
|
|
* br_on_exn 0 0
|
|
|
|
|
* rethrow
|
|
|
|
|
* end
|
|
|
|
|
* local.tee $ex
|
|
|
|
|
* i32.const classIndex(Exception)
|
|
|
|
|
* call $.instanceof
|
|
|
|
|
* i32.eqz
|
|
|
|
|
* if
|
|
|
|
|
* local.get $ex
|
|
|
|
|
* throw 0
|
2019-03-02 21:54:27 +01:00
|
|
|
|
* end
|
|
|
|
|
* code2
|
|
|
|
|
* end
|
|
|
|
|
* code3
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
2018-11-03 18:01:42 +01:00
|
|
|
|
* @param parent
|
|
|
|
|
* the parent branch
|
|
|
|
|
* @param tryBlock
|
|
|
|
|
* the virtual TRY operation
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
|
|
|
|
*/
|
2020-04-11 17:04:21 +02:00
|
|
|
|
private void calculateTry( final BranchNode parent, TryCatchParsedBlock tryBlock, List<ParsedBlock> parsedOperations ) {
|
2018-11-03 18:01:42 +01:00
|
|
|
|
TryCatchFinally tryCatch = tryBlock.tryCatch;
|
|
|
|
|
|
2020-04-19 12:05:35 +02:00
|
|
|
|
ArrayList<TryCatchFinally> catches = new ArrayList<>();
|
|
|
|
|
catches.add( tryCatch );
|
|
|
|
|
|
|
|
|
|
int gotoPos = tryCatch.getHandler()-3; //tryCatch.getEnd() points some time before and some time after the goto
|
2018-11-03 18:01:42 +01:00
|
|
|
|
int endPos = parent.endPos;
|
2020-04-19 12:05:35 +02:00
|
|
|
|
|
|
|
|
|
// find all try blocks and the end position of the last catch/finally handler
|
2019-03-09 13:22:40 +01:00
|
|
|
|
int idx;
|
|
|
|
|
for( idx = 0; idx < parsedOperations.size(); idx++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( idx );
|
2019-11-28 20:07:51 +01:00
|
|
|
|
|
|
|
|
|
if( parsedBlock.op == JavaBlockOperator.TRY ) {
|
|
|
|
|
TryCatchFinally tryCatch2 = ((TryCatchParsedBlock)parsedBlock).tryCatch;
|
|
|
|
|
if( tryCatch.getStart() == tryCatch2.getStart() && tryCatch.getEnd() == tryCatch2.getEnd() ) {
|
2020-04-19 12:05:35 +02:00
|
|
|
|
catches.add( tryCatch2 );
|
|
|
|
|
parsedOperations.remove( idx-- );
|
|
|
|
|
continue;
|
2019-11-28 20:07:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 21:54:27 +01:00
|
|
|
|
if( parsedBlock.startPosition == gotoPos && parsedBlock.op == JavaBlockOperator.GOTO && parsedBlock.startPosition < parsedBlock.endPosition ) {
|
2019-03-09 13:22:40 +01:00
|
|
|
|
parsedOperations.remove( idx );
|
2018-11-03 18:01:42 +01:00
|
|
|
|
endPos = parsedBlock.endPosition;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( parsedBlock.startPosition > gotoPos ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-19 12:05:35 +02:00
|
|
|
|
|
|
|
|
|
// define the try/catch/end block on the right position
|
2019-03-02 21:54:27 +01:00
|
|
|
|
int startPos = tryBlock.startPosition;
|
|
|
|
|
int catchPos = tryCatch.getHandler();
|
|
|
|
|
|
2019-03-09 13:22:40 +01:00
|
|
|
|
BranchNode tryNode = new BranchNode( startPos, catchPos, WasmBlockOperator.TRY, null );
|
2020-04-18 13:09:11 +02:00
|
|
|
|
parent.add( tryNode );
|
2019-03-09 13:22:40 +01:00
|
|
|
|
calculate( tryNode, parsedOperations.subList( 0, idx ) );
|
|
|
|
|
|
2021-02-27 22:22:24 +01:00
|
|
|
|
BranchNode catchNode = new BranchNode( catchPos, endPos, WasmBlockOperator.CATCH, WasmBlockOperator.END, 0 );
|
2020-04-18 13:09:11 +02:00
|
|
|
|
parent.add( catchNode );
|
2019-03-02 21:54:27 +01:00
|
|
|
|
|
2020-04-19 12:05:35 +02:00
|
|
|
|
// Create a block/end structure for every CATCH without the first CATCH
|
|
|
|
|
BranchNode node = catchNode;
|
|
|
|
|
for( int i = catches.size()-1; i > 0; i-- ) {
|
|
|
|
|
TryCatchFinally tryCat = catches.get( i );
|
|
|
|
|
|
2021-06-13 19:36:03 +02:00
|
|
|
|
if( tryCatch.getHandler() == tryCat.getHandler() ) {
|
|
|
|
|
// multiple exception in catch block like "catch ( ArrayIndexOutOfBoundsException | IllegalArgumentException ex )"
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 12:05:35 +02:00
|
|
|
|
int blockGotoPos = tryCat.getHandler()-3;
|
2020-04-19 17:35:56 +02:00
|
|
|
|
for( idx = 0; idx < parsedOperations.size(); idx++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( idx );
|
2020-04-19 12:05:35 +02:00
|
|
|
|
if( parsedBlock.startPosition == blockGotoPos && parsedBlock.op == JavaBlockOperator.GOTO && parsedBlock.startPosition < parsedBlock.endPosition ) {
|
2020-04-19 17:35:56 +02:00
|
|
|
|
parsedOperations.remove( idx );
|
2020-04-19 12:05:35 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( parsedBlock.startPosition > blockGotoPos ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BranchNode block = new BranchNode( catchPos + 1, tryCat.getHandler(), WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
2020-04-25 18:03:16 +02:00
|
|
|
|
block.add( new BranchNode( tryCat.getHandler(), tryCat.getHandler(), WasmBlockOperator.BR, null, catches.size() - i ) );
|
|
|
|
|
node.add( 0, block );
|
2020-04-19 12:05:35 +02:00
|
|
|
|
node = block;
|
|
|
|
|
|
|
|
|
|
int instrPos = findIdxOfCodePos( tryCat.getHandler() ) + 1;
|
|
|
|
|
instructions.remove( instrPos ); // every catch block store the exception from the stack but in WASM we can do this only once for all exceptions
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 17:35:56 +02:00
|
|
|
|
// calculate branch operations inside the CATCH/FINALLY blocks
|
2020-04-25 18:03:16 +02:00
|
|
|
|
calculateTrySubOperations( catchNode, node, parsedOperations );
|
2020-04-19 17:35:56 +02:00
|
|
|
|
|
2020-04-19 12:05:35 +02:00
|
|
|
|
// Create the unboxing and the type check of the exceptions from the catch blocks
|
2019-03-09 13:22:40 +01:00
|
|
|
|
if( tryCatch.isFinally() ) {
|
2020-04-19 17:35:56 +02:00
|
|
|
|
int instrPos = findIdxOfCodePos( catchPos ) + 1;
|
|
|
|
|
WasmInstruction instr = instructions.get( instrPos );
|
|
|
|
|
if( instr.getType() == Type.Block && ((WasmBlockInstruction)instr).getOperation() == WasmBlockOperator.DROP ) {
|
|
|
|
|
// occur with a RETURN in a finally block
|
|
|
|
|
// We does not need to unbox if the value will be drop
|
|
|
|
|
} else {
|
2021-02-27 20:40:30 +01:00
|
|
|
|
addUnboxExnref( catchNode, tryCatch );
|
2020-04-19 17:35:56 +02:00
|
|
|
|
}
|
2019-03-09 13:22:40 +01:00
|
|
|
|
} else {
|
2021-02-27 20:40:30 +01:00
|
|
|
|
addUnboxExnref( catchNode, tryCatch );
|
2020-04-11 17:04:21 +02:00
|
|
|
|
|
|
|
|
|
// add a "if $exception instanceof type" check to the WASM code
|
2020-04-12 10:44:53 +02:00
|
|
|
|
int instrPos = findIdxOfCodePos( catchPos ) + 1;
|
2020-04-19 12:05:35 +02:00
|
|
|
|
WasmLoadStoreInstruction ex = (WasmLoadStoreInstruction)instructions.get( instrPos );
|
2020-04-25 18:03:16 +02:00
|
|
|
|
|
|
|
|
|
node.add( new BranchNode( 0, 0, null, null ) {
|
|
|
|
|
int handle(int codePosition, java.util.List<WasmInstruction> instructions, int idx, int lineNumber) {
|
|
|
|
|
if( codePosition == catchPos + 1 ) {
|
|
|
|
|
FunctionName instanceOf = options.getInstanceOf();
|
|
|
|
|
|
|
|
|
|
instructions.add( idx++, new WasmBlockInstruction( WasmBlockOperator.BLOCK, null, catchPos, lineNumber ) );
|
2021-07-10 19:10:01 +02:00
|
|
|
|
int brIf = -1;
|
|
|
|
|
int handler = -1;
|
2020-04-25 18:03:16 +02:00
|
|
|
|
for( int i = 0; i < catches.size(); i++ ) {
|
|
|
|
|
TryCatchFinally tryCat = catches.get( i );
|
|
|
|
|
String exceptionTypeName = tryCat.getType().getName();
|
|
|
|
|
StructType type = options.types.valueOf( exceptionTypeName );
|
|
|
|
|
instructions.add( idx++, new WasmLoadStoreInstruction( VariableOperator.get, ex.getSlot(), localVariables, catchPos, lineNumber ) );
|
|
|
|
|
instructions.add( idx++, new WasmConstInstruction( type.getClassIndex(), catchPos, lineNumber ) );
|
|
|
|
|
instructions.add( idx++, new WasmCallInstruction( instanceOf, catchPos, lineNumber, options.types, false, exceptionTypeName ) );
|
2021-07-10 19:10:01 +02:00
|
|
|
|
if( handler != tryCat.getHandler() ) {
|
|
|
|
|
// if not multiple exception in catch block like "catch ( ArrayIndexOutOfBoundsException | IllegalArgumentException ex )"
|
|
|
|
|
handler = tryCat.getHandler();
|
|
|
|
|
brIf++;
|
|
|
|
|
}
|
|
|
|
|
instructions.add( idx++, new WasmBlockInstruction( WasmBlockOperator.BR_IF, brIf, catchPos, lineNumber ) );
|
2020-04-25 18:03:16 +02:00
|
|
|
|
}
|
|
|
|
|
instructions.add( idx++, new WasmLoadStoreInstruction( VariableOperator.get, ex.getSlot(), localVariables, catchPos, lineNumber ) );
|
|
|
|
|
instructions.add( idx++, new WasmBlockInstruction( WasmBlockOperator.THROW, null, catchPos, lineNumber ) );
|
|
|
|
|
instructions.add( idx++, new WasmBlockInstruction( WasmBlockOperator.END, null, catchPos, lineNumber ) );
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
} );
|
2019-03-09 13:22:40 +01:00
|
|
|
|
}
|
2019-03-02 21:54:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 18:03:16 +02:00
|
|
|
|
/**
|
|
|
|
|
* Calculate branch operations inside the CATCH/FINALLY blocks.
|
|
|
|
|
*
|
|
|
|
|
* @param catchNode
|
|
|
|
|
* the parent node
|
|
|
|
|
* @param node
|
|
|
|
|
* the most inner node
|
|
|
|
|
* @param parsedOperations
|
|
|
|
|
* the not consumed operations in the parent branch
|
|
|
|
|
*/
|
|
|
|
|
private void calculateTrySubOperations( BranchNode catchNode, BranchNode node, List<ParsedBlock> parsedOperations ) {
|
|
|
|
|
int idx;
|
|
|
|
|
do {
|
|
|
|
|
for( idx = 0; idx < parsedOperations.size(); idx++ ) {
|
|
|
|
|
ParsedBlock parsedBlock = parsedOperations.get( idx );
|
2021-06-13 19:36:03 +02:00
|
|
|
|
if( parsedBlock.startPosition >= node.endPos ) {
|
2020-04-25 18:03:16 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if( idx > 0 ) {
|
|
|
|
|
calculate( node, parsedOperations.subList( 0, idx ) );
|
|
|
|
|
}
|
|
|
|
|
if( node == catchNode ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
node = node.parent;
|
|
|
|
|
} while( node != catchNode );
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 17:35:56 +02:00
|
|
|
|
/**
|
|
|
|
|
* Add an unboxing of the WASm exnref on the stack
|
|
|
|
|
*
|
|
|
|
|
* @param catchNode
|
|
|
|
|
* the catch node
|
2021-02-27 20:40:30 +01:00
|
|
|
|
* @param tryCatch
|
|
|
|
|
* the catch or finally block
|
2020-04-19 17:35:56 +02:00
|
|
|
|
*/
|
2021-02-27 20:40:30 +01:00
|
|
|
|
private void addUnboxExnref( BranchNode catchNode, TryCatchFinally tryCatch ) {
|
2020-04-19 17:35:56 +02:00
|
|
|
|
// unboxing the exnref on the stack to a reference of the exception
|
|
|
|
|
int catchPos = catchNode.startPos;
|
2020-04-26 18:37:10 +02:00
|
|
|
|
if( !options.useEH() ) {
|
|
|
|
|
BranchNode unBoxing = new BranchNode( catchPos, catchPos, WasmBlockOperator.UNREACHABLE, null );
|
|
|
|
|
catchNode.add( 0, unBoxing );
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-02-27 22:22:24 +01:00
|
|
|
|
// AnyType excepType = getCatchType( tryCatch );
|
|
|
|
|
// BlockType blockType = options.types.blockType( Arrays.asList( ValueType.exnref ), Arrays.asList( excepType ) );
|
|
|
|
|
// BranchNode unBoxing = new BranchNode( catchPos, catchPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, blockType );
|
|
|
|
|
// catchNode.add( 0, unBoxing );
|
|
|
|
|
//
|
|
|
|
|
// //TODO localVariables.getTempVariable( ValueType.exnref, catchPos, endPos ); https://github.com/WebAssembly/wabt/issues/1388
|
|
|
|
|
// unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.BR_ON_EXN, null, 0 ) );
|
|
|
|
|
// unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.RETHROW, null ) );
|
2020-04-19 17:35:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-27 20:40:30 +01:00
|
|
|
|
private AnyType getCatchType( TryCatchFinally tryCatch ) {
|
|
|
|
|
if( options.useGC() ) {
|
|
|
|
|
ConstantClass excepClass = tryCatch.getType();
|
|
|
|
|
String excepName = excepClass != null ? excepClass.getName() : "java/lang/Throwable";
|
|
|
|
|
return options.types.valueOf( excepName );
|
|
|
|
|
}
|
|
|
|
|
return ValueType.externref;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 21:54:27 +01:00
|
|
|
|
/**
|
2021-02-27 20:40:30 +01:00
|
|
|
|
* Get the catch type if there are a start of a catch block on the code position.
|
2019-03-02 21:54:27 +01:00
|
|
|
|
*
|
|
|
|
|
* @param codePosition
|
|
|
|
|
* the code position
|
2021-02-27 20:40:30 +01:00
|
|
|
|
* @return the type or null
|
2019-03-02 21:54:27 +01:00
|
|
|
|
*/
|
2021-02-27 20:40:30 +01:00
|
|
|
|
@Nullable
|
|
|
|
|
AnyType getCatchType( int codePosition ) {
|
2019-03-02 21:54:27 +01:00
|
|
|
|
for( TryCatchFinally tryCatch : exceptionTable ) {
|
|
|
|
|
if( tryCatch.getHandler() == codePosition ) {
|
2021-02-27 20:40:30 +01:00
|
|
|
|
return getCatchType( tryCatch );
|
2019-03-02 21:54:27 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-27 20:40:30 +01:00
|
|
|
|
return null;
|
2018-11-03 18:01:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 15:29:12 +02:00
|
|
|
|
/**
|
|
|
|
|
* Add the break to the block hierarchy. Add an extra block if needed and correct the break deep of other
|
|
|
|
|
* instructions.
|
|
|
|
|
*
|
|
|
|
|
* @param breakBlock
|
|
|
|
|
* the description of the break.
|
|
|
|
|
*/
|
|
|
|
|
private void calculateBreak( BreakBlock breakBlock ) {
|
|
|
|
|
int deep = -1;
|
|
|
|
|
int gotoEndPos = breakBlock.endPosition;
|
|
|
|
|
BranchNode branch = breakBlock.branch;
|
|
|
|
|
BranchNode parent = branch;
|
|
|
|
|
while( parent != null && parent.elseEndPos < gotoEndPos ) {
|
|
|
|
|
deep++;
|
|
|
|
|
parent = parent.parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( parent != null && parent.elseEndPos > gotoEndPos ) {
|
2022-01-01 21:03:06 +01:00
|
|
|
|
|
|
|
|
|
// check if we break into an ELSE block which is possible in Java with a GOTO, occur with concatenated conditional operators
|
|
|
|
|
for( int i = 1; i < parent.size(); i++ ) {
|
|
|
|
|
BranchNode node = parent.get( i );
|
|
|
|
|
if( gotoEndPos == node.startPos ) {
|
|
|
|
|
if( node.startOp == WasmBlockOperator.ELSE ) {
|
|
|
|
|
// we can't break into an else block that we break to the IF and push a zero to the stack
|
|
|
|
|
node = parent.get( i - 1 ); // should be the IF to the ELSE
|
|
|
|
|
breakBlock.endPosition = node.startPos;
|
|
|
|
|
breakBlock.breakToElseBlock = true;
|
|
|
|
|
calculateBreak( breakBlock );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 15:29:12 +02:00
|
|
|
|
BranchNode middleNode = new BranchNode( parent.startPos, gotoEndPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
2022-01-01 21:03:06 +01:00
|
|
|
|
while( !parent.isEmpty() ) {
|
|
|
|
|
BranchNode child = parent.get( 0 );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
if( child.endPos > gotoEndPos ) {
|
2022-01-01 21:03:06 +01:00
|
|
|
|
break;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
}
|
|
|
|
|
middleNode.add( child );
|
2022-01-01 21:03:06 +01:00
|
|
|
|
parent.remove( 0 );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
}
|
2022-01-01 21:03:06 +01:00
|
|
|
|
parent.add( 0, middleNode );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
parent = middleNode;
|
|
|
|
|
patchBrDeep( middleNode );
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-01 21:03:06 +01:00
|
|
|
|
if( breakBlock.breakToElseBlock ) {
|
|
|
|
|
// push zero that we switch into the ELSE block
|
|
|
|
|
insertConstBeforePosition( 0, breakBlock.breakPos, -1 );
|
|
|
|
|
}
|
2021-11-20 21:45:59 +01:00
|
|
|
|
BranchNode breakNode = new BranchNode( breakBlock.breakPos, breakBlock.breakPos, WasmBlockOperator.BR, null, deep + 1 );
|
2021-10-24 15:29:12 +02:00
|
|
|
|
branch.add( breakNode );
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-25 12:57:04 +02:00
|
|
|
|
/**
|
|
|
|
|
* Check on every instruction position if there any branch is ending
|
|
|
|
|
*
|
|
|
|
|
* @param byteCode
|
|
|
|
|
* the byte code stream
|
|
|
|
|
*/
|
2018-06-21 18:49:55 +02:00
|
|
|
|
void handle( CodeInputStream byteCode ) {
|
2018-07-15 18:06:25 +02:00
|
|
|
|
int codePosition = -1;
|
|
|
|
|
for( int idx = 0; idx < instructions.size(); idx++ ) {
|
2019-03-31 11:23:45 +02:00
|
|
|
|
WasmInstruction instr = instructions.get( idx );
|
|
|
|
|
int lineNumber;
|
|
|
|
|
int nextCodePosition = instr.getCodePosition();
|
2018-07-15 18:06:25 +02:00
|
|
|
|
if( nextCodePosition <= codePosition ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-08-02 22:39:04 +02:00
|
|
|
|
lineNumber = instr.getLineNumber();
|
|
|
|
|
do {
|
|
|
|
|
// call it for every position for the case that a Jump is call to a intermediate position
|
|
|
|
|
codePosition++;
|
|
|
|
|
idx = root.handle( codePosition, instructions, idx, lineNumber );
|
|
|
|
|
} while( codePosition < nextCodePosition );
|
2018-07-15 18:06:25 +02:00
|
|
|
|
}
|
2019-03-31 11:23:45 +02:00
|
|
|
|
root.handle( byteCode.getCodePosition(), instructions, instructions.size(), byteCode.getLineNumber() );
|
2019-07-23 18:23:59 +02:00
|
|
|
|
|
|
|
|
|
root.calculateBlockType( instructions );
|
2018-03-25 12:57:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-03-30 17:33:23 +02:00
|
|
|
|
* Description of single block/branch from the parsed Java byte code. The parsed branches are plain.
|
2018-03-25 12:57:04 +02:00
|
|
|
|
*/
|
2020-05-21 20:51:48 +02:00
|
|
|
|
private static class ParsedBlock implements Comparable<ParsedBlock> {
|
2018-05-04 20:52:54 +02:00
|
|
|
|
private JavaBlockOperator op;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
2018-08-03 17:25:31 +02:00
|
|
|
|
int startPosition;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
2018-08-03 17:25:31 +02:00
|
|
|
|
int endPosition;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
|
2019-03-19 19:35:42 +01:00
|
|
|
|
int nextPosition;
|
|
|
|
|
|
2018-08-03 17:25:31 +02:00
|
|
|
|
int lineNumber;
|
2018-05-01 11:46:42 +02:00
|
|
|
|
|
2019-03-19 19:35:42 +01:00
|
|
|
|
private ParsedBlock( JavaBlockOperator op, int startPosition, int offset, int nextPosition, int lineNumber ) {
|
2018-03-25 12:57:04 +02:00
|
|
|
|
this.op = op;
|
2018-03-27 20:04:35 +02:00
|
|
|
|
this.startPosition = startPosition;
|
|
|
|
|
this.endPosition = startPosition + offset;
|
2019-03-19 19:35:42 +01:00
|
|
|
|
this.nextPosition = nextPosition;
|
2018-05-01 11:46:42 +02:00
|
|
|
|
this.lineNumber = lineNumber;
|
2018-03-25 12:57:04 +02:00
|
|
|
|
}
|
2020-05-21 20:51:48 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public int compareTo( ParsedBlock o ) {
|
|
|
|
|
return startPosition < o.startPosition ? -1 : startPosition > o.startPosition ? 1 : // first order on the start position
|
|
|
|
|
-Integer.compare( endPosition, o.endPosition ); // then reverse on the end position that outer blocks occur first
|
|
|
|
|
}
|
2018-03-25 12:57:04 +02:00
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2018-08-03 15:17:07 +02:00
|
|
|
|
/**
|
|
|
|
|
* Description of a parsed IF operation.
|
|
|
|
|
*/
|
|
|
|
|
private static class IfParsedBlock extends ParsedBlock {
|
|
|
|
|
|
|
|
|
|
private WasmNumericInstruction instr;
|
|
|
|
|
|
2020-03-26 18:21:50 +01:00
|
|
|
|
private JumpInstruction jump;
|
|
|
|
|
|
2018-08-03 15:17:07 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create new instance
|
|
|
|
|
*
|
|
|
|
|
* @param startPosition
|
|
|
|
|
* the byte position of the start position
|
|
|
|
|
* @param offset
|
|
|
|
|
* the relative jump position
|
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the Java line number for possible error messages
|
|
|
|
|
* @param instr
|
|
|
|
|
* the compare instruction
|
|
|
|
|
*/
|
2020-03-26 18:21:50 +01:00
|
|
|
|
private IfParsedBlock( int startPosition, int offset, int lineNumber, WasmNumericInstruction instr, JumpInstruction jump ) {
|
2019-03-19 19:35:42 +01:00
|
|
|
|
super( JavaBlockOperator.IF, startPosition, offset, startPosition + 3, lineNumber );
|
2018-08-03 15:17:07 +02:00
|
|
|
|
this.instr = instr;
|
2020-03-26 18:21:50 +01:00
|
|
|
|
this.jump = jump;
|
2018-08-03 15:17:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 17:25:31 +02:00
|
|
|
|
/**
|
|
|
|
|
* Negate the compare operation.
|
|
|
|
|
*/
|
2018-08-03 18:04:08 +02:00
|
|
|
|
private void negateCompare() {
|
2018-08-03 17:25:31 +02:00
|
|
|
|
NumericOperator newOp;
|
|
|
|
|
switch( instr.numOp ) {
|
|
|
|
|
case eq:
|
|
|
|
|
newOp = NumericOperator.ne;
|
|
|
|
|
break;
|
|
|
|
|
case ne:
|
|
|
|
|
newOp = NumericOperator.eq;
|
|
|
|
|
break;
|
|
|
|
|
case gt:
|
2018-08-14 21:28:17 +02:00
|
|
|
|
newOp = NumericOperator.le;
|
2018-08-03 17:25:31 +02:00
|
|
|
|
break;
|
2018-08-14 21:28:17 +02:00
|
|
|
|
case lt:
|
|
|
|
|
newOp = NumericOperator.ge;
|
2018-08-03 17:25:31 +02:00
|
|
|
|
break;
|
2018-08-14 21:28:17 +02:00
|
|
|
|
case le:
|
2018-08-03 17:25:31 +02:00
|
|
|
|
newOp = NumericOperator.gt;
|
|
|
|
|
break;
|
2018-08-14 21:28:17 +02:00
|
|
|
|
case ge:
|
|
|
|
|
newOp = NumericOperator.lt;
|
2018-08-03 17:25:31 +02:00
|
|
|
|
break;
|
2018-12-15 22:33:25 +01:00
|
|
|
|
case ifnull:
|
|
|
|
|
newOp = NumericOperator.ifnonnull;
|
|
|
|
|
break;
|
|
|
|
|
case ifnonnull:
|
|
|
|
|
newOp = NumericOperator.ifnull;
|
|
|
|
|
break;
|
2018-12-17 21:22:10 +01:00
|
|
|
|
case ref_eq:
|
|
|
|
|
newOp = NumericOperator.ref_ne;
|
|
|
|
|
break;
|
|
|
|
|
case ref_ne:
|
|
|
|
|
newOp = NumericOperator.ref_eq;
|
|
|
|
|
break;
|
2018-08-03 17:25:31 +02:00
|
|
|
|
default:
|
2020-04-10 22:22:45 +02:00
|
|
|
|
throw new WasmException( "Not a compare operation: " + instr.numOp, lineNumber );
|
2018-08-03 17:25:31 +02:00
|
|
|
|
}
|
|
|
|
|
instr.numOp = newOp;
|
|
|
|
|
}
|
2018-08-03 15:17:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 20:52:54 +02:00
|
|
|
|
/**
|
|
|
|
|
* Description of a parsed switch structure.
|
|
|
|
|
*/
|
|
|
|
|
private static class SwitchParsedBlock extends ParsedBlock {
|
|
|
|
|
private int[] keys;
|
|
|
|
|
|
|
|
|
|
private int[] positions;
|
|
|
|
|
|
|
|
|
|
private int defaultPosition;
|
|
|
|
|
|
|
|
|
|
public SwitchParsedBlock( int startPosition, int offset, int lineNumber, int[] keys, int[] positions, int defaultPosition ) {
|
2019-03-19 19:35:42 +01:00
|
|
|
|
super( JavaBlockOperator.SWITCH, startPosition, offset, startPosition, lineNumber );
|
2018-05-04 20:52:54 +02:00
|
|
|
|
this.keys = keys;
|
|
|
|
|
this.positions = positions;
|
|
|
|
|
this.defaultPosition = defaultPosition;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 18:01:42 +01:00
|
|
|
|
/**
|
|
|
|
|
* Description of a parsed try-Catch structure.
|
|
|
|
|
*/
|
|
|
|
|
private static class TryCatchParsedBlock extends ParsedBlock {
|
|
|
|
|
private final TryCatchFinally tryCatch;
|
|
|
|
|
|
|
|
|
|
TryCatchParsedBlock( TryCatchFinally tryCatch ) {
|
2020-05-21 20:51:48 +02:00
|
|
|
|
super( JavaBlockOperator.TRY, tryCatch.getStart(), tryCatch.getEnd() - tryCatch.getStart(), tryCatch.getStart(), -1 );
|
2018-11-03 18:01:42 +01:00
|
|
|
|
this.tryCatch = tryCatch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-30 17:33:23 +02:00
|
|
|
|
/**
|
|
|
|
|
* Described a code branch/block node in a tree structure.
|
|
|
|
|
*/
|
|
|
|
|
private static class BranchNode extends ArrayList<BranchNode> {
|
|
|
|
|
|
2018-05-06 14:33:22 +02:00
|
|
|
|
private final int startPos;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
|
2019-07-23 18:23:59 +02:00
|
|
|
|
private int endPos;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2018-05-04 20:52:54 +02:00
|
|
|
|
private final WasmBlockOperator startOp;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2019-07-23 18:23:59 +02:00
|
|
|
|
private WasmBlockOperator endOp;
|
|
|
|
|
|
2020-04-10 16:30:59 +02:00
|
|
|
|
/**
|
|
|
|
|
* Extra data depending of the operator. For example the return type of a block.
|
|
|
|
|
*/
|
2019-07-23 18:23:59 +02:00
|
|
|
|
private Object data;
|
|
|
|
|
|
|
|
|
|
private BranchNode parent;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
2020-04-10 16:30:59 +02:00
|
|
|
|
/**
|
|
|
|
|
* A instruction for which the return type must be calculated.
|
|
|
|
|
*/
|
2019-07-23 18:23:59 +02:00
|
|
|
|
private WasmBlockInstruction startBlock;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
|
2020-04-10 16:30:59 +02:00
|
|
|
|
/**
|
|
|
|
|
* The position of the startBlock in the instructions
|
|
|
|
|
*/
|
2019-07-23 18:23:59 +02:00
|
|
|
|
private int startIdx;
|
2018-05-20 11:54:06 +02:00
|
|
|
|
|
2021-10-24 15:29:12 +02:00
|
|
|
|
private int elseEndPos;
|
|
|
|
|
|
2021-11-16 21:28:11 +01:00
|
|
|
|
/** jump position for a CONTINUE in a loop */
|
|
|
|
|
private int continuePos;
|
|
|
|
|
|
2018-05-04 20:52:54 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create a new description.
|
|
|
|
|
*
|
|
|
|
|
* @param startPos
|
|
|
|
|
* the start position in the Java code. Limit also the children.
|
|
|
|
|
* @param endPos
|
|
|
|
|
* the end position in the Java code. Limit also the children.
|
|
|
|
|
* @param startOp
|
|
|
|
|
* The WASM operation on the start position. Can be null if there is nothing in WASM.
|
|
|
|
|
* @param endOp
|
|
|
|
|
* the WASM operation on the end position. Can be null if there is nothing in WASM.
|
|
|
|
|
*/
|
|
|
|
|
BranchNode( int startPos, int endPos, WasmBlockOperator startOp, WasmBlockOperator endOp ) {
|
|
|
|
|
this( startPos, endPos, startOp, endOp, null );
|
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new description.
|
|
|
|
|
*
|
|
|
|
|
* @param startPos
|
|
|
|
|
* the start position in the Java code. Limit also the children.
|
|
|
|
|
* @param endPos
|
|
|
|
|
* the end position in the Java code. Limit also the children.
|
|
|
|
|
* @param startOp
|
|
|
|
|
* The WASM operation on the start position. Can be null if there is nothing in WASM.
|
|
|
|
|
* @param endOp
|
|
|
|
|
* the WASM operation on the end position. Can be null if there is nothing in WASM.
|
2018-05-04 20:52:54 +02:00
|
|
|
|
* @param data
|
|
|
|
|
* extra data depending of the start operator
|
2018-03-30 17:33:23 +02:00
|
|
|
|
*/
|
2018-05-04 20:52:54 +02:00
|
|
|
|
BranchNode( int startPos, int endPos, WasmBlockOperator startOp, WasmBlockOperator endOp, Object data ) {
|
2018-03-30 17:33:23 +02:00
|
|
|
|
this.startPos = startPos;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
this.endPos = this.elseEndPos = endPos;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
this.startOp = startOp;
|
|
|
|
|
this.endOp = endOp;
|
2018-05-04 20:52:54 +02:00
|
|
|
|
this.data = data;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 11:54:06 +02:00
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
2020-04-25 18:03:16 +02:00
|
|
|
|
public boolean add( BranchNode node ) {
|
|
|
|
|
node.parent = this;
|
|
|
|
|
return super.add( node );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void add( int index, BranchNode node ) {
|
|
|
|
|
node.parent = this;
|
|
|
|
|
super.add( index, node );
|
2018-05-20 11:54:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 18:49:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* Handle branches on the current codePosition
|
|
|
|
|
*
|
2018-07-15 18:06:25 +02:00
|
|
|
|
* @param codePosition
|
2018-06-21 18:49:55 +02:00
|
|
|
|
* current code position
|
|
|
|
|
* @param instructions
|
|
|
|
|
* the target for instructions
|
2018-07-15 18:06:25 +02:00
|
|
|
|
* @param idx
|
|
|
|
|
* index in the current instruction
|
2019-03-31 11:23:45 +02:00
|
|
|
|
* @param lineNumber
|
|
|
|
|
* the line number in the Java source code
|
2018-07-15 18:06:25 +02:00
|
|
|
|
* @return the new index in the instructions
|
2018-06-21 18:49:55 +02:00
|
|
|
|
*/
|
2019-07-23 18:23:59 +02:00
|
|
|
|
int handle( int codePosition, List<WasmInstruction> instructions, int idx, int lineNumber ) {
|
2018-07-15 18:06:25 +02:00
|
|
|
|
if( codePosition < startPos || codePosition > endPos ) {
|
|
|
|
|
return idx;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2018-07-15 18:06:25 +02:00
|
|
|
|
if( codePosition == startPos && startOp != null ) {
|
2019-07-23 18:23:59 +02:00
|
|
|
|
startBlock = new WasmBlockInstruction( startOp, data, codePosition, lineNumber );
|
|
|
|
|
instructions.add( idx++, startBlock );
|
|
|
|
|
startIdx = idx;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
|
|
|
|
for( BranchNode branch : this ) {
|
2019-03-31 11:23:45 +02:00
|
|
|
|
idx = branch.handle( codePosition, instructions, idx, lineNumber );
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2018-07-15 18:06:25 +02:00
|
|
|
|
if( codePosition == endPos && endOp != null ) {
|
2019-03-31 11:23:45 +02:00
|
|
|
|
instructions.add( idx++, new WasmBlockInstruction( endOp, null, codePosition, lineNumber ) );
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2018-07-15 18:06:25 +02:00
|
|
|
|
return idx;
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2019-07-23 18:23:59 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2021-11-20 21:45:59 +01:00
|
|
|
|
* Calculate the block type (return type). The value type that is on the stack after the block.
|
2019-07-23 18:23:59 +02:00
|
|
|
|
*
|
|
|
|
|
* @param instructions
|
|
|
|
|
* the instructions of the function
|
|
|
|
|
*/
|
|
|
|
|
void calculateBlockType( List<WasmInstruction> instructions ) {
|
|
|
|
|
for( int i = size() - 1; i >= 0; i-- ) {
|
|
|
|
|
BranchNode branch = get( i );
|
|
|
|
|
branch.calculateBlockType( instructions );
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-20 21:45:59 +01:00
|
|
|
|
if( startBlock == null ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch( startBlock.getOperation() ) {
|
|
|
|
|
case IF:
|
2021-11-21 16:29:10 +01:00
|
|
|
|
case BLOCK:
|
2020-04-10 13:40:55 +02:00
|
|
|
|
try {
|
|
|
|
|
ArrayDeque<AnyType> stack = new ArrayDeque<>();
|
|
|
|
|
stack.push( ValueType.empty );
|
|
|
|
|
INSTRUCTIONS: for( int i = startIdx; i < instructions.size(); i++ ) {
|
|
|
|
|
WasmInstruction instr = instructions.get( i );
|
|
|
|
|
int codePos = instr.getCodePosition();
|
2021-11-20 21:45:59 +01:00
|
|
|
|
if( codePos > endPos ) {
|
2020-04-10 13:40:55 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
int popCount = instr.getPopCount();
|
|
|
|
|
for( int p = 0; p < popCount; p++ ) {
|
|
|
|
|
stack.pop();
|
|
|
|
|
}
|
|
|
|
|
AnyType pushValue = instr.getPushValueType();
|
|
|
|
|
if( pushValue != null ) {
|
|
|
|
|
stack.push( pushValue );
|
|
|
|
|
}
|
2019-09-29 13:47:45 +02:00
|
|
|
|
|
2020-04-10 13:40:55 +02:00
|
|
|
|
if( instr.getType() == Type.Block ) {
|
2022-01-01 21:03:06 +01:00
|
|
|
|
WasmBlockInstruction blockInstr = (WasmBlockInstruction)instr;
|
|
|
|
|
switch( blockInstr.getOperation() ) {
|
2020-04-10 13:40:55 +02:00
|
|
|
|
case RETURN:
|
|
|
|
|
// set "empty" block type
|
|
|
|
|
while( stack.size() > 1 ) {
|
|
|
|
|
stack.pop();
|
|
|
|
|
}
|
|
|
|
|
break INSTRUCTIONS;
|
|
|
|
|
case IF:
|
|
|
|
|
case BLOCK:
|
|
|
|
|
case LOOP:
|
2020-04-10 16:32:06 +02:00
|
|
|
|
case TRY:
|
2020-04-10 13:40:55 +02:00
|
|
|
|
// skip the content of the block, important to not count ELSE blocks
|
|
|
|
|
i = findEndInstruction( instructions, i );
|
|
|
|
|
break;
|
2021-11-20 21:45:59 +01:00
|
|
|
|
case END:
|
|
|
|
|
case ELSE:
|
|
|
|
|
break INSTRUCTIONS;
|
2022-01-01 21:03:06 +01:00
|
|
|
|
case BR:
|
|
|
|
|
Integer data = (Integer)blockInstr.getData();
|
|
|
|
|
if( data > 0 ) {
|
|
|
|
|
// TODO we should check the ELSE block in this case if there is any
|
|
|
|
|
// set "empty" block type
|
|
|
|
|
while( stack.size() > 1 ) {
|
|
|
|
|
stack.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break INSTRUCTIONS;
|
|
|
|
|
default:
|
2020-04-10 13:40:55 +02:00
|
|
|
|
}
|
2019-09-29 13:47:45 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-10 13:40:55 +02:00
|
|
|
|
startBlock.setData( stack.pop() );
|
|
|
|
|
} catch( Throwable th ) {
|
|
|
|
|
throw WasmException.create( th, startBlock.getLineNumber() );
|
2019-07-23 18:23:59 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-29 13:47:45 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find the END instruction of the block.
|
|
|
|
|
*
|
|
|
|
|
* @param instructions
|
|
|
|
|
* the list of instructions
|
|
|
|
|
* @param idx
|
|
|
|
|
* the index of the block start
|
|
|
|
|
* @return the END index
|
|
|
|
|
*/
|
|
|
|
|
private int findEndInstruction( List<WasmInstruction> instructions, int idx ) {
|
|
|
|
|
int count = 0;
|
|
|
|
|
for( ; idx < instructions.size(); idx++ ) {
|
|
|
|
|
WasmInstruction instr = instructions.get( idx );
|
|
|
|
|
if( instr.getType() == Type.Block ) {
|
|
|
|
|
switch( ((WasmBlockInstruction)instr).getOperation() ) {
|
|
|
|
|
case IF:
|
|
|
|
|
case BLOCK:
|
|
|
|
|
case LOOP:
|
2020-04-10 16:32:06 +02:00
|
|
|
|
case TRY:
|
2019-09-29 13:47:45 +02:00
|
|
|
|
count++;
|
|
|
|
|
break;
|
|
|
|
|
case END:
|
|
|
|
|
count--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if( count == 0 ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
2018-03-30 17:33:23 +02:00
|
|
|
|
}
|
2019-08-02 11:11:10 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Positions inside a IF control structure.
|
|
|
|
|
*/
|
|
|
|
|
private static class IfPositions {
|
2020-05-25 19:28:47 +02:00
|
|
|
|
/** Count of boolean operations in the IF top level condition. This can be (&&) or (||) operations. */
|
2019-08-02 11:11:10 +02:00
|
|
|
|
private int ifCount;
|
|
|
|
|
|
2020-05-25 19:28:47 +02:00
|
|
|
|
/** The position of the first instruction in the THEN part. */
|
2019-08-02 11:11:10 +02:00
|
|
|
|
private int thenPos;
|
|
|
|
|
|
2020-05-25 19:28:47 +02:00
|
|
|
|
/** The position of the first instruction in the ELSE part. */
|
2019-08-02 11:11:10 +02:00
|
|
|
|
private int elsePos;
|
|
|
|
|
}
|
2021-10-24 15:29:12 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Described a break to a block that will be added later.
|
|
|
|
|
*/
|
|
|
|
|
private static class BreakBlock {
|
|
|
|
|
|
2022-01-02 18:17:55 +01:00
|
|
|
|
private final int breakPos;
|
2021-11-20 21:45:59 +01:00
|
|
|
|
|
2022-01-02 18:17:55 +01:00
|
|
|
|
private int endPosition;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
|
2022-01-02 18:17:55 +01:00
|
|
|
|
private BranchNode branch;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
|
2022-01-02 18:17:55 +01:00
|
|
|
|
private boolean breakToElseBlock;
|
2022-01-01 21:03:06 +01:00
|
|
|
|
|
2021-10-24 15:29:12 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create Break
|
|
|
|
|
*
|
|
|
|
|
* @param branch
|
|
|
|
|
* the parent block which should contain the break
|
2021-11-20 21:45:59 +01:00
|
|
|
|
* @param breakPos
|
|
|
|
|
* the position where the break should be inserted.
|
2021-10-24 15:29:12 +02:00
|
|
|
|
* @param endPosition
|
|
|
|
|
* the Jump position
|
|
|
|
|
*/
|
2021-11-20 21:45:59 +01:00
|
|
|
|
public BreakBlock( BranchNode branch, int breakPos, int endPosition ) {
|
|
|
|
|
this.breakPos = breakPos;
|
2021-10-24 15:29:12 +02:00
|
|
|
|
this.endPosition = endPosition;
|
|
|
|
|
this.branch = branch;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-25 12:57:04 +02:00
|
|
|
|
}
|