• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

i-net-software / JWebAssembly / 521

pending completion
521

push

travis-ci-com

Horcrux7
Unsafe code for AtomicReference

5 of 5 new or added lines in 2 files covered. (100.0%)

5772 of 6694 relevant lines covered (86.23%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.3
/src/de/inetsoftware/jwebassembly/watparser/WatParser.java
1
/*
2
   Copyright 2018 - 2023 Volker Berlin (i-net software)
3

4
   Licensed under the Apache License, Version 2.0 (the "License");
5
   you may not use this file except in compliance with the License.
6
   You may obtain a copy of the License at
7

8
       http://www.apache.org/licenses/LICENSE-2.0
9

10
   Unless required by applicable law or agreed to in writing, software
11
   distributed under the License is distributed on an "AS IS" BASIS,
12
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
   See the License for the specific language governing permissions and
14
   limitations under the License.
15

16
*/
17
package de.inetsoftware.jwebassembly.watparser;
18

19
import java.util.ArrayList;
20
import java.util.Iterator;
21
import java.util.List;
22

23
import javax.annotation.Nonnegative;
24
import javax.annotation.Nonnull;
25
import javax.annotation.Nullable;
26

27
import de.inetsoftware.classparser.MethodInfo;
28
import de.inetsoftware.jwebassembly.WasmException;
29
import de.inetsoftware.jwebassembly.module.FunctionName;
30
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
31
import de.inetsoftware.jwebassembly.module.WasmCodeBuilder;
32
import de.inetsoftware.jwebassembly.wasm.AnyType;
33
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
34
import de.inetsoftware.jwebassembly.wasm.ArrayType;
35
import de.inetsoftware.jwebassembly.wasm.MemoryOperator;
36
import de.inetsoftware.jwebassembly.wasm.NamedStorageType;
37
import de.inetsoftware.jwebassembly.wasm.NumericOperator;
38
import de.inetsoftware.jwebassembly.wasm.StructOperator;
39
import de.inetsoftware.jwebassembly.wasm.ValueType;
40
import de.inetsoftware.jwebassembly.wasm.VariableOperator;
41
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
42

43
/**
44
 * Parser for text format of a function.
45
 * 
46
 * @author Volker Berlin
47
 */
48
public class WatParser extends WasmCodeBuilder {
1✔
49

50
    /**
51
     * Parse the given wasm text format and generate a list of WasmInstuctions
52
     * 
53
     * @param wat
54
     *            the text format content of a function
55
     * @param method
56
     *            the method with signature as fallback for a missing variable table
57
     * @param signature
58
     *            alternative for method signature, can be null if method is set
59
     * @param lineNumber
60
     *            the line number for an error message
61
     */
62
    public void parse( String wat, MethodInfo method, Iterator<AnyType> signature, int lineNumber ) {
63
        try {
64
            reset( null, method, signature );
1✔
65

66
            List<String> tokens = splitTokens( wat );
1✔
67
            for( int i = 0; i < tokens.size(); i++ ) {
1✔
68
                int javaCodePos = i;
1✔
69
                String tok = tokens.get( i );
1✔
70
                switch( tok ) {
1✔
71
                    case "local.get":
72
                        addLocalInstruction( VariableOperator.get, getInt( tokens, ++i), javaCodePos, lineNumber );
1✔
73
                        break;
1✔
74
                    case "local.set":
75
                        addLocalInstruction( VariableOperator.set, getInt( tokens, ++i), javaCodePos, lineNumber );
1✔
76
                        break;
1✔
77
                    case "local.tee":
78
                        addLocalInstruction( VariableOperator.tee, getInt( tokens, ++i), javaCodePos, lineNumber );
1✔
79
                        break;
1✔
80
                    case "global.get":
81
                        addGlobalInstruction( true, get( tokens, ++i), javaCodePos, lineNumber );
×
82
                        break;
×
83
                    case "global.set":
84
                        addGlobalInstruction( false, get( tokens, ++i), javaCodePos, lineNumber );
×
85
                        break;
×
86
                    case "i32.const":
87
                        addConstInstruction( getInt( tokens, ++i), ValueType.i32, javaCodePos, lineNumber );
1✔
88
                        break;
1✔
89
                    case "i32.add":
90
                        addNumericInstruction( NumericOperator.add, ValueType.i32, javaCodePos, lineNumber );
1✔
91
                        break;
1✔
92
                    case "i32.eq":
93
                        addNumericInstruction( NumericOperator.eq, ValueType.i32, javaCodePos, lineNumber );
1✔
94
                        break;
1✔
95
                    case "i32.div_s":
96
                        addNumericInstruction( NumericOperator.div, ValueType.i32, javaCodePos, lineNumber );
1✔
97
                        break;
1✔
98
                    case "i32.eqz":
99
                        addNumericInstruction( NumericOperator.eqz, ValueType.i32, javaCodePos, lineNumber );
1✔
100
                        break;
1✔
101
                    case "i32.mul":
102
                        addNumericInstruction( NumericOperator.mul, ValueType.i32, javaCodePos, lineNumber );
1✔
103
                        break;
1✔
104
                    case "i32.ne":
105
                        addNumericInstruction( NumericOperator.ne, ValueType.i32, javaCodePos, lineNumber );
1✔
106
                        break;
1✔
107
                    case "i32.reinterpret_f32":
108
                        addConvertInstruction( ValueTypeConvertion.f2i_re, javaCodePos, lineNumber );
1✔
109
                        break;
1✔
110
                    case "i32.trunc_sat_f32_s":
111
                        addConvertInstruction( ValueTypeConvertion.f2i, javaCodePos, lineNumber );
1✔
112
                        break;
1✔
113
                    case "i64.const":
114
                        addConstInstruction( Long.parseLong( get( tokens, ++i ) ), ValueType.i64, javaCodePos, lineNumber );
1✔
115
                        break;
1✔
116
                    case "i64.add":
117
                        addNumericInstruction( NumericOperator.add, ValueType.i64, javaCodePos, lineNumber );
×
118
                        break;
×
119
                    case "i64.eq":
120
                        addNumericInstruction( NumericOperator.eq, ValueType.i64, javaCodePos, lineNumber );
×
121
                        break;
×
122
                    case "i64.div_s":
123
                        addNumericInstruction( NumericOperator.div, ValueType.i64, javaCodePos, lineNumber );
1✔
124
                        break;
1✔
125
                    case "i64.eqz":
126
                        addNumericInstruction( NumericOperator.eqz, ValueType.i64, javaCodePos, lineNumber );
1✔
127
                        break;
1✔
128
                    case "i64.extend_i32_s":
129
                        addConvertInstruction( ValueTypeConvertion.i2l, javaCodePos, lineNumber );
1✔
130
                        break;
1✔
131
                    case "i64.reinterpret_f64":
132
                        addConvertInstruction( ValueTypeConvertion.d2l_re, javaCodePos, lineNumber );
1✔
133
                        break;
1✔
134
                    case "i64.trunc_sat_f64_s":
135
                        addConvertInstruction( ValueTypeConvertion.d2l, javaCodePos, lineNumber );
1✔
136
                        break;
1✔
137
                    case "f32.abs":
138
                        addNumericInstruction( NumericOperator.abs, ValueType.f32, javaCodePos, lineNumber );
1✔
139
                        break;
1✔
140
                    case "f32.ceil":
141
                        addNumericInstruction( NumericOperator.ceil, ValueType.f32, javaCodePos, lineNumber );
1✔
142
                        break;
1✔
143
                    case "f32.const":
144
                        addConstInstruction( Float.parseFloat( get( tokens, ++i ) ), ValueType.f32, javaCodePos, lineNumber );
1✔
145
                        break;
1✔
146
                    case "f32.convert_i32_s":
147
                        addConvertInstruction( ValueTypeConvertion.i2f, javaCodePos, lineNumber );
1✔
148
                        break;
1✔
149
                    case "f32.div":
150
                        addNumericInstruction( NumericOperator.div, ValueType.f32, javaCodePos, lineNumber );
1✔
151
                        break;
1✔
152
                    case "f32.floor":
153
                        addNumericInstruction( NumericOperator.floor, ValueType.f32, javaCodePos, lineNumber );
1✔
154
                        break;
1✔
155
                    case "f32.max":
156
                        addNumericInstruction( NumericOperator.max, ValueType.f32, javaCodePos, lineNumber );
1✔
157
                        break;
1✔
158
                    case "f32.min":
159
                        addNumericInstruction( NumericOperator.min, ValueType.f32, javaCodePos, lineNumber );
1✔
160
                        break;
1✔
161
                    case "f32.mul":
162
                        addNumericInstruction( NumericOperator.mul, ValueType.f32, javaCodePos, lineNumber );
1✔
163
                        break;
1✔
164
                    case "f32.nearest":
165
                        addNumericInstruction( NumericOperator.nearest, ValueType.f32, javaCodePos, lineNumber );
1✔
166
                        break;
1✔
167
                    case "f32.reinterpret_i32":
168
                        addConvertInstruction( ValueTypeConvertion.i2f_re, javaCodePos, lineNumber );
1✔
169
                        break;
1✔
170
                    case "f32.copysign":
171
                        addNumericInstruction( NumericOperator.copysign, ValueType.f32, javaCodePos, lineNumber );
1✔
172
                        break;
1✔
173
                    case "f32.sqrt":
174
                        addNumericInstruction( NumericOperator.sqrt, ValueType.f32, javaCodePos, lineNumber );
1✔
175
                        break;
1✔
176
                    case "f32.sub":
177
                        addNumericInstruction( NumericOperator.sub, ValueType.f32, javaCodePos, lineNumber );
1✔
178
                        break;
1✔
179
                    case "f32.trunc":
180
                        addNumericInstruction( NumericOperator.trunc, ValueType.f32, javaCodePos, lineNumber );
1✔
181
                        break;
1✔
182
                    case "f64.abs":
183
                        addNumericInstruction( NumericOperator.abs, ValueType.f64, javaCodePos, lineNumber );
1✔
184
                        break;
1✔
185
                    case "f64.ceil":
186
                        addNumericInstruction( NumericOperator.ceil, ValueType.f64, javaCodePos, lineNumber );
1✔
187
                        break;
1✔
188
                    case "f64.const":
189
                        addConstInstruction( Double.parseDouble( get( tokens, ++i ) ), ValueType.f64, javaCodePos, lineNumber );
1✔
190
                        break;
1✔
191
                    case "f64.convert_i64_s":
192
                        addConvertInstruction( ValueTypeConvertion.l2d, javaCodePos, lineNumber );
1✔
193
                        break;
1✔
194
                    case "f64.div":
195
                        addNumericInstruction( NumericOperator.div, ValueType.f64, javaCodePos, lineNumber );
1✔
196
                        break;
1✔
197
                    case "f64.floor":
198
                        addNumericInstruction( NumericOperator.floor, ValueType.f64, javaCodePos, lineNumber );
1✔
199
                        break;
1✔
200
                    case "f64.max":
201
                        addNumericInstruction( NumericOperator.max, ValueType.f64, javaCodePos, lineNumber );
1✔
202
                        break;
1✔
203
                    case "f64.min":
204
                        addNumericInstruction( NumericOperator.min, ValueType.f64, javaCodePos, lineNumber );
1✔
205
                        break;
1✔
206
                    case "f64.mul":
207
                        addNumericInstruction( NumericOperator.mul, ValueType.f64, javaCodePos, lineNumber );
1✔
208
                        break;
1✔
209
                    case "f64.nearest":
210
                        addNumericInstruction( NumericOperator.nearest, ValueType.f64, javaCodePos, lineNumber );
1✔
211
                        break;
1✔
212
                    case "f64.reinterpret_i64":
213
                        addConvertInstruction( ValueTypeConvertion.l2d_re, javaCodePos, lineNumber );
1✔
214
                        break;
1✔
215
                    case "f64.copysign":
216
                        addNumericInstruction( NumericOperator.copysign, ValueType.f64, javaCodePos, lineNumber );
1✔
217
                        break;
1✔
218
                    case "f64.sqrt":
219
                        addNumericInstruction( NumericOperator.sqrt, ValueType.f64, javaCodePos, lineNumber );
1✔
220
                        break;
1✔
221
                    case "f64.sub":
222
                        addNumericInstruction( NumericOperator.sub, ValueType.f64, javaCodePos, lineNumber );
1✔
223
                        break;
1✔
224
                    case "f64.trunc":
225
                        addNumericInstruction( NumericOperator.trunc, ValueType.f64, javaCodePos, lineNumber );
1✔
226
                        break;
1✔
227
                    case "ref.is_null":
228
                        addNumericInstruction( NumericOperator.ifnull, ValueType.i32, javaCodePos, lineNumber );
1✔
229
                        break;
1✔
230
                    case "ref.eq":
231
                        addNumericInstruction( NumericOperator.ref_eq, ValueType.i32, javaCodePos, lineNumber );
×
232
                        break;
×
233
                    case "table.get":
234
                        addTableInstruction( true, getInt( tokens, ++i), javaCodePos, lineNumber );
1✔
235
                        break;
1✔
236
                    case "table.set":
237
                        addTableInstruction( false, getInt( tokens, ++i), javaCodePos, lineNumber );
1✔
238
                        break;
1✔
239
                    case "call":
240
                        try {
241
                            StringBuilder builder = new StringBuilder( get( tokens, ++i ) );
1✔
242
                            String str;
243
                            do {
244
                                str = get( tokens, ++i );
1✔
245
                                builder.append( str );
1✔
246
                            } while ( !")".equals( str ) );
1✔
247
                            builder.append( get( tokens, ++i ) );
1✔
248
                            FunctionName name = new FunctionName( builder.substring( 1 ) );
1✔
249
                            addCallInstruction( name, false, javaCodePos, lineNumber );
1✔
250
                        } catch( Exception ex ) {
×
251
                            throw WasmException.create( "The syntax for a function name is $package.ClassName.methodName(paramSignature)returnSignature", ex );
×
252
                        }
1✔
253
                        break;
254
                    case "return":
255
                        addBlockInstruction( WasmBlockOperator.RETURN, null, javaCodePos, lineNumber );
1✔
256
                        break;
1✔
257
                    case "if":
258
                        Object data = ValueType.empty;
1✔
259
                        if( "(".equals( get( tokens, i+1 ) ) ) {
1✔
260
                            i++;
1✔
261
                            if( "result".equals( get( tokens, ++i ) ) && ")".equals( get( tokens, ++i + 1) ) ) {
1✔
262
                                data = ValueType.valueOf( get( tokens, i++ ) );
1✔
263
                            } else {
264
                                throw new WasmException( "Unknown WASM token: " + get( tokens, i-1 ), lineNumber );
×
265
                            }
266
                        }
267
                        addBlockInstruction( WasmBlockOperator.IF, data, javaCodePos, lineNumber );
1✔
268
                        break;
1✔
269
                    case "else":
270
                        addBlockInstruction( WasmBlockOperator.ELSE, null, javaCodePos, lineNumber );
1✔
271
                        break;
1✔
272
                    case "end":
273
                        addBlockInstruction( WasmBlockOperator.END, null, javaCodePos, lineNumber );
1✔
274
                        break;
1✔
275
                    case "drop":
276
                        addBlockInstruction( WasmBlockOperator.DROP, null, javaCodePos, lineNumber );
1✔
277
                        break;
1✔
278
                    case "loop":
279
                        addBlockInstruction( WasmBlockOperator.LOOP, null, javaCodePos, lineNumber );
1✔
280
                        break;
1✔
281
                    case "br":
282
                        addBlockInstruction( WasmBlockOperator.BR, getInt( tokens, ++i ), javaCodePos, lineNumber );
1✔
283
                        break;
1✔
284
                    case "br_if":
285
                        addBlockInstruction( WasmBlockOperator.BR_IF, getInt( tokens, ++i ), javaCodePos, lineNumber );
1✔
286
                        break;
1✔
287
                    case "br_on_null":
288
                        addBlockInstruction( WasmBlockOperator.BR_ON_NULL, getInt( tokens, ++i ), javaCodePos, lineNumber );
×
289
                        break;
×
290
                    case "throw":
291
                        addBlockInstruction( WasmBlockOperator.THROW, null, javaCodePos, lineNumber );
1✔
292
                        break;
1✔
293
                    case "unreachable":
294
                        addBlockInstruction( WasmBlockOperator.UNREACHABLE, null, javaCodePos, lineNumber );
1✔
295
                        break;
1✔
296
                    case "i32.load":
297
                        i = addMemoryInstruction( MemoryOperator.load, ValueType.i32, tokens, i, lineNumber );
1✔
298
                        break;
1✔
299
                    case "i32.load8_u":
300
                        i = addMemoryInstruction( MemoryOperator.load8_u, ValueType.i32, tokens, i, lineNumber );
1✔
301
                        break;
1✔
302
                    case "struct.get":
303
                    case "struct.set":
304
                        StructOperator op = "struct.get".equals( tok ) ? StructOperator.GET : StructOperator.SET;
1✔
305
                        String typeName = get( tokens, ++i );
1✔
306
                        String fieldName = get( tokens, ++i );
1✔
307
                        NamedStorageType fieldNameType = null;
1✔
308
                        List<NamedStorageType> fields = getTypeManager().valueOf( typeName ).getFields();
1✔
309
                        if( fields != null ) { // field is null on prepare
1✔
310
                            for( NamedStorageType namedStorageType : fields ) {
1✔
311
                                if( namedStorageType.getName().equals( fieldName ) ) {
1✔
312
                                    fieldNameType = namedStorageType;
1✔
313
                                    break;
1✔
314
                                }
315
                            }
×
316
                        }
317
                        if( fieldNameType == null ) {
1✔
318
                            fieldNameType = new NamedStorageType( ValueType.externref, "", fieldName );
1✔
319
                        }
320
                        addStructInstruction( op, typeName, fieldNameType, javaCodePos, lineNumber );
1✔
321
                        break;
1✔
322
                    case "array.len":
323
                        typeName = get( tokens, ++i );
×
324
                        AnyType type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType();
×
325
                        addArrayInstruction( ArrayOperator.LEN, type, javaCodePos, lineNumber );
×
326
                        break;
×
327
                    case "array.new_default_with_rtt":
328
                        typeName = get( tokens, ++i );
1✔
329
                        type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType();
1✔
330
                        addArrayInstruction( ArrayOperator.NEW_ARRAY_WITH_RTT, type, javaCodePos, lineNumber );
1✔
331
                        break;
1✔
332
                    case "rtt.canon":
333
                        typeName = get( tokens, ++i );
1✔
334
                        addStructInstruction( StructOperator.RTT_CANON, typeName, null, javaCodePos, lineNumber );
1✔
335
                        break;
1✔
336
                    case "struct.new_with_rtt":
337
                        typeName = get( tokens, ++i );
1✔
338
                        addStructInstruction( StructOperator.NEW_WITH_RTT, typeName, null, javaCodePos, lineNumber );
1✔
339
                        break;
1✔
340
                    default:
341
                        throw new WasmException( "Unknown WASM token: " + tok, lineNumber );
1✔
342
                }
343
            }
344
        } catch( Throwable ex ) {
1✔
345
            throw WasmException.create( ex, lineNumber );
1✔
346
        }
1✔
347
    }
1✔
348

349
    /**
350
     * Get the token at given position as int.
351
     * 
352
     * @param tokens
353
     *            the token list
354
     * @param idx
355
     *            the position in the tokens
356
     * @return the int value
357
     */
358
    private int getInt( List<String> tokens, @Nonnegative int idx ) {
359
        return Integer.parseInt( get( tokens, idx ) );
1✔
360
    }
361

362
    /**
363
     * Get the token at given position
364
     * 
365
     * @param tokens
366
     *            the token list
367
     * @param idx
368
     *            the position in the tokens
369
     * @return the token
370
     */
371
    @Nonnull
372
    private String get( List<String> tokens, @Nonnegative int idx ) {
373
        if( idx >= tokens.size() ) {
1✔
374
            String previous = tokens.get( Math.min( idx, tokens.size() ) - 1 );
1✔
375
            throw new WasmException( "Missing Token in wasm text format after token: " + previous, -1 );
1✔
376
        }
377
        return tokens.get( idx );
1✔
378
    }
379

380
    /**
381
     * Split the string in tokens.
382
     * 
383
     * @param wat
384
     *            string with wasm text format
385
     * @return the token list.
386
     */
387
    private List<String> splitTokens( @Nullable String wat ) {
388
        ArrayList<String> tokens = new ArrayList<>();
1✔
389
        int count = wat.length();
1✔
390

391
        int off = 0;
1✔
392
        for( int i = 0; i < count; i++ ) {
1✔
393
            char ch = wat.charAt( i );
1✔
394
            switch( ch ) {
1✔
395
                case ' ':
396
                case '\n':
397
                case '\r':
398
                case '\t':
399
                case '(':
400
                case ')':
401
                    if( off < i ) {
1✔
402
                        tokens.add( wat.substring( off, i ) );
1✔
403
                    }
404
                    off = i + 1;
1✔
405
                    switch(ch) {
1✔
406
                        case '(':
407
                            tokens.add( "(" );
1✔
408
                            break;
1✔
409
                        case ')':
410
                            tokens.add( ")" );
1✔
411
                            break;
412
                    }
413
                    break;
414
            }
415
        }
416
        if( off < count ) {
1✔
417
            tokens.add( wat.substring( off, count ) );
1✔
418
        }
419
        return tokens;
1✔
420
    }
421

422
    /**
423
     * Parse the optional tokens of a load memory instruction and add it.
424
     * 
425
     * @param op
426
     *            the operation
427
     * @param type
428
     *            the type of the static field
429
     * @param tokens
430
     *            the token list
431
     * @param i
432
     *            the position in the tokens
433
     * @param lineNumber
434
     *            the line number in the Java source code
435
     * @return the current index to the tokens
436
     */
437
    private int addMemoryInstruction( MemoryOperator op, ValueType type, List<String> tokens, int i, int lineNumber ) {
438
        int offset = 0;
1✔
439
        int alignment = 0;
1✔
440
        if( i < tokens.size() ) {
1✔
441
            String str = tokens.get( i + 1 );
1✔
442
            if( str.startsWith( "offset=" ) ) {
1✔
443
                offset = Integer.parseInt( str.substring( 7 ) );
1✔
444
                i++;
1✔
445
            }
446
            str = tokens.get( i + 1 );
1✔
447
            if( str.startsWith( "align=" ) ) {
1✔
448
                int align = Integer.parseInt( str.substring( 6 ) );
1✔
449
                switch( align ) {
1✔
450
                    case 1:
451
                        alignment = 0;
1✔
452
                        break;
1✔
453
                    case 2:
454
                        alignment = 1;
×
455
                        break;
×
456
                    case 4:
457
                        alignment = 2;
1✔
458
                        break;
1✔
459
                    default:
460
                        throw new WasmException( "alignment must be power-of-two", lineNumber );
×
461
                }
462
                i++;
1✔
463
            }
464
        }
465
        addMemoryInstruction( op, type, offset, alignment, i, lineNumber );
1✔
466
        return i;
1✔
467
    }
468
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc