├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── ci.jsonnet ├── example.js ├── pom.xml ├── runJsWithCoverage.sh ├── simpletool └── src ├── main └── java │ ├── com │ └── oracle │ │ └── truffle │ │ └── st │ │ ├── Coverage.java │ │ ├── CoverageEventFactory.java │ │ ├── CoverageNode.java │ │ ├── GatherSourceSectionsListener.java │ │ └── SimpleCoverageInstrument.java │ └── module-info.java └── test └── java ├── com └── oracle │ └── truffle │ └── st │ └── test │ └── SimpleCoverageInstrumentTest.java └── module-info.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This repository contains a snapshot of SimpleTool that is updated only after major changes. The development version is part of the Truffle repository: https://github.com/graalvm/truffle 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | 15 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 16 | one is included with the Software each a "Larger Work" to which the Software 17 | is contributed by such licensors), 18 | 19 | without restriction, including without limitation the rights to copy, create 20 | derivative works of, display, perform, and distribute the Software and make, 21 | use, sell, offer for sale, import, export, have made, and have sold the 22 | Software and the Larger Work(s), and to sublicense the foregoing rights on 23 | either these or other terms. 24 | 25 | This license is subject to the following condition: 26 | 27 | The above copyright notice and either this complete permission notice or at a 28 | minimum a reference to the UPL must be included in all copies or substantial 29 | portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Tool 2 | 3 | A simple demonstration code coverage tool built using Truffle for GraalVM. 4 | 5 | The source code is documented to explain the how and why of writing a Truffle 6 | tool. A good way to find out more is to read the source with comments. We also 7 | like to encourage people to clone the repository and start hacking. 8 | 9 | This repository is licensed under the permissive UPL licence. Fork it to begin 10 | your own Truffle tool. 11 | 12 | For instructions on how to get started please refer to [our website](https://www.graalvm.org/docs/graalvm-as-a-platform/implement-instrument/) 13 | -------------------------------------------------------------------------------- /ci.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | local basicBuild = { 3 | targets: ['gate'], 4 | timelimit: '00:59:59', 5 | run: [ 6 | ['mvn', 'clean'], 7 | ['mvn', 'package'], 8 | ['./simpletool', 'example.js'], 9 | ], 10 | }, 11 | 12 | local graalvm = { 13 | downloads+: { 14 | JAVA_HOME: { name: 'graalvm-community-java21', version: '23.1.0', platformspecific: true }, 15 | }, 16 | }, 17 | 18 | local linux = { 19 | capabilities+: ['linux', 'amd64'], 20 | packages+: { 21 | maven: '==3.3.9', 22 | }, 23 | docker: { 24 | image: "buildslave_ol7", 25 | mount_modules: true, 26 | }, 27 | }, 28 | 29 | local darwin = { 30 | capabilities+: ['darwin_sierra', 'amd64'], 31 | environment+: { 32 | MACOSX_DEPLOYMENT_TARGET: '10.11', 33 | JAVA_HOME: '$JAVA_HOME/Contents/Home' 34 | }, 35 | }, 36 | 37 | builds: [ 38 | basicBuild + linux + graalvm + { name: 'linux' }, 39 | 40 | basicBuild + darwin + graalvm + { name: 'darwin' }, 41 | ], 42 | } 43 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | var N = 2000; 42 | var EXPECTED = 17393; 43 | 44 | function Natural() { 45 | x = 2; 46 | return { 47 | 'next' : function() { return x++; } 48 | }; 49 | } 50 | 51 | function Filter(number, filter) { 52 | var self = this; 53 | this.number = number; 54 | this.filter = filter; 55 | this.accept = function(n) { 56 | var filter = self; 57 | for (;;) { 58 | if (n % filter.number === 0) { 59 | return false; 60 | } 61 | filter = filter.filter; 62 | if (filter === null) { 63 | break; 64 | } 65 | } 66 | return true; 67 | }; 68 | return this; 69 | } 70 | 71 | function Primes(natural) { 72 | var self = this; 73 | this.natural = natural; 74 | this.filter = null; 75 | this.next = function() { 76 | for (;;) { 77 | var n = self.natural.next(); 78 | if (self.filter === null || self.filter.accept(n)) { 79 | self.filter = new Filter(n, self.filter); 80 | return n; 81 | } 82 | } 83 | }; 84 | } 85 | 86 | var holdsAFunctionThatIsNeverCalled = function(natural) { 87 | var self = this; 88 | this.natural = natural; 89 | this.filter = null; 90 | this.next = function() { 91 | for (;;) { 92 | var n = self.natural.next(); 93 | if (self.filter === null || self.filter.accept(n)) { 94 | self.filter = new Filter(n, self.filter); 95 | return n; 96 | } 97 | } 98 | }; 99 | } 100 | 101 | var holdsAFunctionThatIsNeverCalledOneLine = function() {return null;} 102 | 103 | function primesMain() { 104 | var primes = new Primes(Natural()); 105 | var primArray = []; 106 | for (var i=0;i<=N;i++) { primArray.push(primes.next()); } 107 | if (primArray[N] != EXPECTED) { throw new Error('wrong prime found: ' + primArray[N]); } 108 | } 109 | primesMain(); 110 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 42 | 45 | 4.0.0 46 | com.oracle 47 | simpletool 48 | ${graalvm.version} 49 | simpletool 50 | 51 | 23.1.0 52 | UTF-8 53 | 17 54 | 17 55 | jdt_apt 56 | 57 | 58 | 59 | junit 60 | junit 61 | 4.13.2 62 | test 63 | 64 | 65 | org.graalvm.polyglot 66 | polyglot 67 | ${graalvm.version} 68 | 69 | 70 | org.graalvm.truffle 71 | truffle-api 72 | ${graalvm.version} 73 | 74 | 75 | org.graalvm.truffle 76 | truffle-runtime 77 | ${graalvm.version} 78 | runtime 79 | 80 | 81 | org.graalvm.sdk 82 | jniutils 83 | ${graalvm.version} 84 | runtime 85 | 86 | 87 | 88 | org.graalvm.js 89 | js-language 90 | ${graalvm.version} 91 | runtime 92 | 93 | 94 | org.graalvm.js 95 | js-launcher 96 | ${graalvm.version} 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-dependency-plugin 104 | 3.2.0 105 | 106 | 107 | copy-dependencies 108 | package 109 | 110 | copy-dependencies 111 | 112 | 113 | ${project.build.directory}/modules 114 | true 115 | 116 | 117 | 118 | 119 | 120 | maven-compiler-plugin 121 | 3.11.0 122 | 123 | 17 124 | 17 125 | 126 | 127 | org.graalvm.truffle 128 | truffle-dsl-processor 129 | ${graalvm.version} 130 | 131 | 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-surefire-plugin 137 | 3.1.2 138 | 139 | true 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /runJsWithCoverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. 4 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 | # 6 | # The Universal Permissive License (UPL), Version 1.0 7 | # 8 | # Subject to the condition set forth below, permission is hereby granted to any 9 | # person obtaining a copy of this software, associated documentation and/or 10 | # data (collectively the "Software"), free of charge and under any and all 11 | # copyright rights in the Software, and any and all patent rights owned or 12 | # freely licensable by each licensor hereunder covering either (i) the 13 | # unmodified Software as contributed to or provided by such licensor, or (ii) 14 | # the Larger Works (as defined below), to deal in both 15 | # 16 | # (a) the Software, and 17 | # 18 | # (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 19 | # one is included with the Software each a "Larger Work" to which the Software 20 | # is contributed by such licensors), 21 | # 22 | # without restriction, including without limitation the rights to copy, create 23 | # derivative works of, display, perform, and distribute the Software and make, 24 | # use, sell, offer for sale, import, export, have made, and have sold the 25 | # Software and the Larger Work(s), and to sublicense the foregoing rights on 26 | # either these or other terms. 27 | # 28 | # This license is subject to the following condition: 29 | # 30 | # The above copyright notice and either this complete permission notice or at a 31 | # minimum a reference to the UPL must be included in all copies or substantial 32 | # portions of the Software. 33 | # 34 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | # SOFTWARE. 41 | # 42 | 43 | ./simpletool example.js 44 | -------------------------------------------------------------------------------- /simpletool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 4 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 | # 6 | # The Universal Permissive License (UPL), Version 1.0 7 | # 8 | # Subject to the condition set forth below, permission is hereby granted to any 9 | # person obtaining a copy of this software, associated documentation and/or 10 | # data (collectively the "Software"), free of charge and under any and all 11 | # copyright rights in the Software, and any and all patent rights owned or 12 | # freely licensable by each licensor hereunder covering either (i) the 13 | # unmodified Software as contributed to or provided by such licensor, or (ii) 14 | # the Larger Works (as defined below), to deal in both 15 | # 16 | # (a) the Software, and 17 | # 18 | # (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 19 | # one is included with the Software each a "Larger Work" to which the Software 20 | # is contributed by such licensors), 21 | # 22 | # without restriction, including without limitation the rights to copy, create 23 | # derivative works of, display, perform, and distribute the Software and make, 24 | # use, sell, offer for sale, import, export, have made, and have sold the 25 | # Software and the Larger Work(s), and to sublicense the foregoing rights on 26 | # either these or other terms. 27 | # 28 | # This license is subject to the following condition: 29 | # 30 | # The above copyright notice and either this complete permission notice or at a 31 | # minimum a reference to the UPL must be included in all copies or substantial 32 | # portions of the Software. 33 | # 34 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | # SOFTWARE. 41 | # 42 | 43 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 44 | 45 | "$JAVA_HOME/bin/java" -p "${DIR}/target/modules:${DIR}/target/classes" -m org.graalvm.js.launcher/com.oracle.truffle.js.shell.JSLauncher --simple-code-coverage "$@" -------------------------------------------------------------------------------- /src/main/java/com/oracle/truffle/st/Coverage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st; 42 | 43 | import java.util.HashSet; 44 | import java.util.Set; 45 | 46 | import com.oracle.truffle.api.source.SourceSection; 47 | 48 | /** 49 | * Contains per {@link com.oracle.truffle.api.source.Source} coverage by keeping track of loaded and 50 | * covered {@link com.oracle.truffle.api.source.SourceSection}s. 51 | */ 52 | public final class Coverage { 53 | private final Set loaded = new HashSet<>(); 54 | private final Set covered = new HashSet<>(); 55 | 56 | void addCovered(SourceSection sourceSection) { 57 | covered.add(sourceSection); 58 | } 59 | 60 | void addLoaded(SourceSection sourceSection) { 61 | loaded.add(sourceSection); 62 | } 63 | 64 | private Set nonCoveredSections() { 65 | final HashSet nonCovered = new HashSet<>(); 66 | nonCovered.addAll(loaded); 67 | nonCovered.removeAll(covered); 68 | return nonCovered; 69 | } 70 | 71 | Set nonCoveredLineNumbers() { 72 | Set linesNotCovered = new HashSet<>(); 73 | for (SourceSection ss : nonCoveredSections()) { 74 | for (int i = ss.getStartLine(); i <= ss.getEndLine(); i++) { 75 | linesNotCovered.add(i); 76 | } 77 | } 78 | return linesNotCovered; 79 | } 80 | 81 | Set loadedLineNumbers() { 82 | Set loadedLines = new HashSet<>(); 83 | for (SourceSection ss : loaded) { 84 | for (int i = ss.getStartLine(); i <= ss.getEndLine(); i++) { 85 | loadedLines.add(i); 86 | } 87 | } 88 | return loadedLines; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/oracle/truffle/st/CoverageEventFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st; 42 | 43 | import com.oracle.truffle.api.instrumentation.EventContext; 44 | import com.oracle.truffle.api.instrumentation.ExecutionEventNode; 45 | import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; 46 | import com.oracle.truffle.api.source.SourceSection; 47 | 48 | /** 49 | * A factory for nodes that track coverage 50 | * 51 | * Because we 52 | * {@link SimpleCoverageInstrument#enable(com.oracle.truffle.api.instrumentation.TruffleInstrument.Env) 53 | * attached} an instance of this factory, each time a AST node of interest is created, it is 54 | * instrumented with a node created by this factory. 55 | */ 56 | final class CoverageEventFactory implements ExecutionEventNodeFactory { 57 | 58 | private SimpleCoverageInstrument simpleCoverageInstrument; 59 | 60 | CoverageEventFactory(SimpleCoverageInstrument simpleCoverageInstrument) { 61 | this.simpleCoverageInstrument = simpleCoverageInstrument; 62 | } 63 | 64 | /** 65 | * @param ec context of the event, used in our case to lookup the {@link SourceSection} that our 66 | * node is instrumenting. 67 | * @return An {@link ExecutionEventNode} 68 | */ 69 | public ExecutionEventNode create(final EventContext ec) { 70 | return new CoverageNode(simpleCoverageInstrument, ec.getInstrumentedSourceSection()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/oracle/truffle/st/CoverageNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st; 42 | 43 | import com.oracle.truffle.api.CompilerDirectives; 44 | import com.oracle.truffle.api.frame.VirtualFrame; 45 | import com.oracle.truffle.api.instrumentation.ExecutionEventNode; 46 | import com.oracle.truffle.api.instrumentation.Instrumenter; 47 | import com.oracle.truffle.api.source.SourceSection; 48 | 49 | /** 50 | * Node that "wraps" AST nodes of interest (Nodes that correspond to expressions in our case as 51 | * defined by the filter given to the {@link Instrumenter} in 52 | * {@link SimpleCoverageInstrument#onCreate(com.oracle.truffle.api.instrumentation.TruffleInstrument.Env) } 53 | * ), and informs the {@link SimpleCoverageInstrument} that we 54 | * {@link SimpleCoverageInstrument#addCovered(SourceSection) covered} it's 55 | * {@link #instrumentedSourceSection source section}. 56 | */ 57 | final class CoverageNode extends ExecutionEventNode { 58 | 59 | private final SimpleCoverageInstrument instrument; 60 | @CompilerDirectives.CompilationFinal private boolean covered; 61 | 62 | /** 63 | * Each node knows which {@link SourceSection} it instruments. 64 | */ 65 | private final SourceSection instrumentedSourceSection; 66 | 67 | CoverageNode(SimpleCoverageInstrument instrument, SourceSection instrumentedSourceSection) { 68 | this.instrument = instrument; 69 | this.instrumentedSourceSection = instrumentedSourceSection; 70 | } 71 | 72 | /** 73 | * The {@link ExecutionEventNode} class let's us define several events that we can intercept. 74 | * The one of interest to us is 75 | * {@link ExecutionEventNode#onReturnValue(com.oracle.truffle.api.frame.VirtualFrame, Object) } 76 | * as we wish to track this nodes {@link #instrumentedSourceSection} in the 77 | * {@link SimpleCoverageInstrument#coverageMap} only once the node is successfully executed (as 78 | * oppose to, for example, 79 | * {@link ExecutionEventNode#onReturnExceptional(com.oracle.truffle.api.frame.VirtualFrame, Throwable) } 80 | * ). 81 | * 82 | * Each node keeps a {@link #covered} flag so that the removal only happens once. The fact that 83 | * the flag is annotated with {@link CompilerDirectives.CompilationFinal} means that this flag 84 | * will be treated as {@code final} during compilation of instrumented source code (i.e. the 85 | * {@code false} branch of the if statement can be optimized away). 86 | * 87 | * The way it's used in this method is a pattern when writing Truffle nodes: 88 | *
    89 | *
  • If we are compiling a covered node, the if condition will evaluate to false and the 90 | * if-guarded code will be optimized away. This means that once this {@link SourceSection} is 91 | * confirmed to be covered, there is no further instrumentation overhead on performance. 92 | *
  • If we are compiling a not-yet-covered node, the if condition will evaluate to true, and 93 | * the if-guarded code will be included for compilation. The first statement in this block is a 94 | * {@link CompilerDirectives#transferToInterpreterAndInvalidate() directive to the compiler} to 95 | * make sure that if this point in the execution is reached, the execution should return to the 96 | * interpreter and the existing compiled code is no longer valid (since once the covered flag is 97 | * set to true, the check is unnecessary). The code following the directive is thus always 98 | * executed in the interpreter: We set the {@link #covered} flag to true, ensuring that the next 99 | * compilation will have no instrumentation overhead on performance.
  • 100 | *
101 | * 102 | * @param vFrame unused 103 | * @param result unused 104 | */ 105 | @Override 106 | public void onReturnValue(VirtualFrame vFrame, Object result) { 107 | if (!covered) { 108 | CompilerDirectives.transferToInterpreterAndInvalidate(); 109 | covered = true; 110 | instrument.addCovered(instrumentedSourceSection); 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/oracle/truffle/st/GatherSourceSectionsListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st; 42 | 43 | import com.oracle.truffle.api.instrumentation.LoadSourceSectionEvent; 44 | import com.oracle.truffle.api.instrumentation.LoadSourceSectionListener; 45 | import com.oracle.truffle.api.source.SourceSection; 46 | 47 | /** 48 | * A listener for new {@link SourceSection}s being loaded. 49 | * 50 | * Because we 51 | * {@link SimpleCoverageInstrument#enable(com.oracle.truffle.api.instrumentation.TruffleInstrument.Env) 52 | * attached} an instance of this listener, each time a new {@link SourceSection} of interest is 53 | * loaded, we are notified in the 54 | * {@link #onLoad(com.oracle.truffle.api.instrumentation.LoadSourceSectionEvent) } method. 55 | */ 56 | final class GatherSourceSectionsListener implements LoadSourceSectionListener { 57 | 58 | private final SimpleCoverageInstrument instrument; 59 | 60 | GatherSourceSectionsListener(SimpleCoverageInstrument instrument) { 61 | this.instrument = instrument; 62 | } 63 | 64 | /** 65 | * Notification that a new {@link LoadSourceSectionEvent} has occurred. 66 | * 67 | * @param event information about the event. We use this information to keep our 68 | * {@link SimpleCoverageInstrument#coverageMap} up to date. 69 | */ 70 | @Override 71 | public void onLoad(LoadSourceSectionEvent event) { 72 | final SourceSection sourceSection = event.getSourceSection(); 73 | instrument.addLoaded(sourceSection); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/oracle/truffle/st/SimpleCoverageInstrument.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st; 42 | 43 | import java.io.PrintStream; 44 | import java.util.Collections; 45 | import java.util.HashMap; 46 | import java.util.Map; 47 | import java.util.Set; 48 | import java.util.function.Function; 49 | 50 | import org.graalvm.options.OptionCategory; 51 | import org.graalvm.options.OptionDescriptors; 52 | import org.graalvm.options.OptionKey; 53 | import org.graalvm.options.OptionStability; 54 | import org.graalvm.options.OptionValues; 55 | 56 | import com.oracle.truffle.api.Option; 57 | import com.oracle.truffle.api.instrumentation.Instrumenter; 58 | import com.oracle.truffle.api.instrumentation.SourceSectionFilter; 59 | import com.oracle.truffle.api.instrumentation.StandardTags.ExpressionTag; 60 | import com.oracle.truffle.api.instrumentation.TruffleInstrument; 61 | import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration; 62 | import com.oracle.truffle.api.nodes.Node; 63 | import com.oracle.truffle.api.source.Source; 64 | import com.oracle.truffle.api.source.SourceSection; 65 | 66 | /** 67 | * Example for simple version of an expression coverage instrument. 68 | * 69 | * The instrument {@link #coverageMap keeps track} of all loaded {@link SourceSection}s and all 70 | * coverd (i.e. executed) {@link SourceSection}s for each {@link Source}. At the end of the 71 | * execution this information can be used to calculate coverage. 72 | * 73 | * The instrument is registered with the Truffle framework using the {@link Registration} 74 | * annotation. The annotation specifies a unique {@link Registration#id}, a human readable 75 | * {@link Registration#name} and {@link Registration#version} for the instrument. It also specifies 76 | * all service classes that the instrument exports to other instruments and, exceptionally, tests. 77 | * In this case the instrument itself is exported as a service and used in the 78 | * SimpleCoverageInstrumentTest. 79 | * 80 | * NOTE: Fot the registration annotation to work the truffle dsl processor must be used (i.e. Must 81 | * be a dependency. This is so in this maven project, as can be seen in the pom file. 82 | */ 83 | @Registration(id = SimpleCoverageInstrument.ID, name = "Simple Code Coverage", version = "0.1", services = SimpleCoverageInstrument.class) 84 | public final class SimpleCoverageInstrument extends TruffleInstrument { 85 | 86 | // @formatter:off 87 | /** 88 | * Look at {@link #onCreate(Env)} and {@link #getOptionDescriptors()} for more info. 89 | */ 90 | @Option(name = "", help = "Enable Simple Coverage (default: false).", category = OptionCategory.USER, stability = OptionStability.STABLE) 91 | static final OptionKey ENABLED = new OptionKey<>(false); 92 | 93 | /** 94 | * Look at {@link #onCreate(Env)} and {@link #getOptionDescriptors()} for more info. 95 | */ 96 | @Option(name = "PrintCoverage", help = "Print coverage to stdout on process exit (default: true).", category = OptionCategory.USER, stability = OptionStability.STABLE) 97 | static final OptionKey PRINT_COVERAGE = new OptionKey<>(true); 98 | // @formatter:on 99 | 100 | public static final String ID = "simple-code-coverage"; 101 | 102 | /** 103 | * The instrument keeps a mapping between a {@link Source} and {@link Coverage coverage} data 104 | * for that source. Coverage tracks loaded and covered {@link SourceSection} during execution. 105 | */ 106 | final Map coverageMap = new HashMap<>(); 107 | 108 | public synchronized Map getCoverageMap() { 109 | return Collections.unmodifiableMap(coverageMap); 110 | } 111 | 112 | /** 113 | * Each instrument must override the 114 | * {@link TruffleInstrument#onCreate(com.oracle.truffle.api.instrumentation.TruffleInstrument.Env)} 115 | * method. 116 | * 117 | * This method is used to properly initialize the instrument. A common practice is to use the 118 | * {@link Option} system to enable and configure the instrument, as is done in this method. 119 | * Defining {@link Option}s as is shown in {@link #ENABLED} and {@link #PRINT_COVERAGE}, and 120 | * their usage can be seen in the SimpleCoverageInstrumentTest when the context is being 121 | * created. Using them from the command line is shown in the simpletool.sh script. 122 | * 123 | * @param env the environment for the instrument. Allows us to read the {@link Option}s, input 124 | * and output streams to be used for reading and writing, as well as 125 | * {@link Env#registerService(java.lang.Object) registering} and 126 | * {@link Env#lookup(com.oracle.truffle.api.InstrumentInfo, java.lang.Class) looking 127 | * up} services. 128 | */ 129 | @Override 130 | protected void onCreate(final Env env) { 131 | final OptionValues options = env.getOptions(); 132 | if (ENABLED.getValue(options)) { 133 | enable(env); 134 | env.registerService(this); 135 | } 136 | } 137 | 138 | /** 139 | * Enable the instrument. 140 | * 141 | * In this method we enable and configure the instrument. We do this by first creating a 142 | * {@link SourceSectionFilter} instance in order to specify exactly which parts of the source 143 | * code we are interested in. In this particular case, we are interested in expressions. Since 144 | * Truffle Instruments are language agnostic, they rely on language implementers to tag AST 145 | * nodes with adequate tags. This, we tell our {@link SourceSectionFilter.Builder} that we are 146 | * care about AST nodes {@link SourceSectionFilter.Builder#tagIs(java.lang.Class...) tagged} 147 | * with {@link ExpressionTag}. We also tell it we don't care about AST nodes 148 | * {@link SourceSectionFilter.Builder#includeInternal(boolean) internal} to languages. 149 | * 150 | * After than, we use the {@link Env enviroment} to obtain the {@link Instrumenter}, which 151 | * allows us to specify in which way we wish to instrument the AST. 152 | * 153 | * Firstly, we 154 | * {@link Instrumenter#attachLoadSourceListener(com.oracle.truffle.api.instrumentation.SourceFilter, com.oracle.truffle.api.instrumentation.LoadSourceListener, boolean) 155 | * attach attach} our own {@link GatherSourceSectionsListener listener} to loading source 156 | * section events. Each the a {@link SourceSection} is loaded, our listener is notified, so our 157 | * instrument is always aware of all loaded code. Note that we have specified the filter defined 158 | * earlier as a constraint, so we are not notified if internal code is loaded. 159 | * 160 | * Secondly, we 161 | * {@link Instrumenter#attachExecutionEventFactory(com.oracle.truffle.api.instrumentation.SourceSectionFilter, com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory) 162 | * attach} our {@link CoverageEventFactory node factory} using the same filter. This factory 163 | * produces {@link Node Truffle Nodes} that will be inserted into the AST at positions specified 164 | * by the filter. Each of the inserted nodes will, once executed, remove the corresponding 165 | * source section from the {@link #coverageMap set of unexecuted source sections}. 166 | * 167 | * @param env The environment, used to get the {@link Instrumenter} 168 | */ 169 | private void enable(final Env env) { 170 | SourceSectionFilter filter = SourceSectionFilter.newBuilder().tagIs(ExpressionTag.class).includeInternal(false).build(); 171 | Instrumenter instrumenter = env.getInstrumenter(); 172 | instrumenter.attachLoadSourceSectionListener(filter, new GatherSourceSectionsListener(this), true); 173 | instrumenter.attachExecutionEventFactory(filter, new CoverageEventFactory(this)); 174 | } 175 | 176 | /** 177 | * Ensures that the coverage info gathered by the instrument is printed at the end of execution. 178 | * 179 | * @param env 180 | */ 181 | @Override 182 | protected void onFinalize(Env env) { 183 | if (PRINT_COVERAGE.getValue(env.getOptions())) { 184 | printResults(env); 185 | } 186 | } 187 | 188 | /** 189 | * Print the coverage results for each source. 190 | * 191 | * The printing is one the the {@link Env#out output stream} specified by the {@link Env 192 | * enviroment}. 193 | * 194 | * @param env 195 | */ 196 | private synchronized void printResults(final Env env) { 197 | final PrintStream printStream = new PrintStream(env.out()); 198 | for (Source source : coverageMap.keySet()) { 199 | printResult(printStream, source); 200 | } 201 | } 202 | 203 | private void printResult(PrintStream printStream, Source source) { 204 | String path = source.getPath(); 205 | int lineCount = source.getLineCount(); 206 | Set nonCoveredLineNumbers = nonCoveredLineNumbers(source); 207 | Set loadedLineNumbers = coverageMap.get(source).loadedLineNumbers(); 208 | double coveredPercentage = 100 * ((double) loadedLineNumbers.size() - nonCoveredLineNumbers.size()) / lineCount; 209 | printStream.println("=="); 210 | printStream.println("Coverage of " + path + " is " + String.format("%.2f%%", coveredPercentage)); 211 | for (int i = 1; i <= source.getLineCount(); i++) { 212 | char covered = getCoverageCharacter(nonCoveredLineNumbers, loadedLineNumbers, i); 213 | printStream.println(String.format("%s %s", covered, source.getCharacters(i))); 214 | } 215 | } 216 | 217 | private static char getCoverageCharacter(Set nonCoveredLineNumbers, Set loadedLineNumbers, int i) { 218 | if (loadedLineNumbers.contains(i)) { 219 | return nonCoveredLineNumbers.contains(i) ? '-' : '+'; 220 | } else { 221 | return ' '; 222 | } 223 | } 224 | 225 | /** 226 | * @param source 227 | * @return A sorted list of line numbers for not-yet-covered lines of source code in the given 228 | * {@link Source} 229 | */ 230 | public synchronized Set nonCoveredLineNumbers(final Source source) { 231 | return coverageMap.get(source).nonCoveredLineNumbers(); 232 | } 233 | 234 | /** 235 | * Which {@link OptionDescriptors} are used for this instrument. 236 | * 237 | * If the {@link TruffleInstrument} uses {@link Option}s, it is nesesery to specify which 238 | * {@link Option}s. The {@link OptionDescriptors} is automatically generated from this class due 239 | * to the {@link Option} annotation. In our case, this is the 240 | * {@code SimpleCodeCoverageInstrumentOptionDescriptors} class. 241 | * 242 | * @return The class generated by the {@link Option.Group} annotation 243 | */ 244 | @Override 245 | protected OptionDescriptors getOptionDescriptors() { 246 | return new SimpleCoverageInstrumentOptionDescriptors(); 247 | } 248 | 249 | /** 250 | * Called when a new {@link SourceSection} is loaded. We can update our {@link #coverageMap}. 251 | * 252 | * @param sourceSection the newly loaded {@link SourceSection} 253 | */ 254 | synchronized void addLoaded(SourceSection sourceSection) { 255 | final Coverage coverage = coverageMap.computeIfAbsent(sourceSection.getSource(), new Function() { 256 | @Override 257 | public Coverage apply(Source s) { 258 | return new Coverage(); 259 | } 260 | }); 261 | coverage.addLoaded(sourceSection); 262 | } 263 | 264 | /** 265 | * Called after a {@link SourceSection} is executed, and thus covered. We can update our 266 | * {@link #coverageMap}. 267 | * 268 | * @param sourceSection the executed {@link SourceSection} 269 | */ 270 | synchronized void addCovered(SourceSection sourceSection) { 271 | final Coverage coverage = coverageMap.get(sourceSection.getSource()); 272 | coverage.addCovered(sourceSection); 273 | } 274 | 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | module org.graalvm.st { 42 | requires java.base; 43 | requires java.logging; 44 | requires jdk.unsupported; 45 | requires org.graalvm.polyglot; 46 | requires org.graalvm.truffle; 47 | exports com.oracle.truffle.st to org.graalvm.st.test; 48 | provides com.oracle.truffle.api.instrumentation.provider.TruffleInstrumentProvider with 49 | com.oracle.truffle.st.SimpleCoverageInstrumentProvider; 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/oracle/truffle/st/test/SimpleCoverageInstrumentTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | package com.oracle.truffle.st.test; 42 | 43 | import java.io.IOException; 44 | import java.util.Map; 45 | import java.util.Set; 46 | 47 | import org.graalvm.polyglot.Context; 48 | import org.graalvm.polyglot.Engine; 49 | import org.graalvm.polyglot.Source; 50 | import org.junit.Assert; 51 | import org.junit.Assume; 52 | import org.junit.Test; 53 | 54 | import com.oracle.truffle.st.Coverage; 55 | import com.oracle.truffle.st.SimpleCoverageInstrument; 56 | 57 | public class SimpleCoverageInstrumentTest { 58 | 59 | private static final String JS_SOURCE = "var N = 2000;\n" + 60 | "var EXPECTED = 17393;\n" + 61 | "\n" + 62 | "function Natural() {\n" + 63 | " x = 2;\n" + 64 | " return {\n" + 65 | " 'next' : function() { return x++; }\n" + 66 | " };\n" + 67 | "}\n" + 68 | "\n" + 69 | "function Filter(number, filter) {\n" + 70 | " var self = this;\n" + 71 | " this.number = number;\n" + 72 | " this.filter = filter;\n" + 73 | " this.accept = function(n) {\n" + 74 | " var filter = self;\n" + 75 | " for (;;) {\n" + 76 | " if (n % filter.number === 0) {\n" + 77 | " return false;\n" + 78 | " }\n" + 79 | " filter = filter.filter;\n" + 80 | " if (filter === null) {\n" + 81 | " break;\n" + 82 | " }\n" + 83 | " }\n" + 84 | " return true;\n" + 85 | " };\n" + 86 | " return this;\n" + 87 | "}\n" + 88 | "\n" + 89 | "function Primes(natural) {\n" + 90 | " var self = this;\n" + 91 | " this.natural = natural;\n" + 92 | " this.filter = null;\n" + 93 | " this.next = function() {\n" + 94 | " for (;;) {\n" + 95 | " var n = self.natural.next();\n" + 96 | " if (self.filter === null || self.filter.accept(n)) {\n" + 97 | " self.filter = new Filter(n, self.filter);\n" + 98 | " return n;\n" + 99 | " }\n" + 100 | " }\n" + 101 | " };\n" + 102 | "}\n" + 103 | "\n" + 104 | "var holdsAFunctionThatIsNeverCalled = function(natural) {\n" + 105 | " var self = this;\n" + 106 | " this.natural = natural;\n" + 107 | " this.filter = null;\n" + 108 | " this.next = function() {\n" + 109 | " for (;;) {\n" + 110 | " var n = self.natural.next();\n" + 111 | " if (self.filter === null || self.filter.accept(n)) {\n" + 112 | " self.filter = new Filter(n, self.filter);\n" + 113 | " return n;\n" + 114 | " }\n" + 115 | " }\n" + 116 | " };\n" + 117 | "}\n" + 118 | "\n" + 119 | "var holdsAFunctionThatIsNeverCalledOneLine = function() {return null;}\n" + 120 | "\n" + 121 | "function primesMain() {\n" + 122 | " var primes = new Primes(Natural());\n" + 123 | " var primArray = [];\n" + 124 | " for (var i=0;i<=N;i++) { primArray.push(primes.next()); }\n" + 125 | " if (primArray[N] != EXPECTED) { throw new Error('wrong prime found: ' + primArray[N]); }\n" + 126 | "}\n" + 127 | "primesMain();\n"; 128 | 129 | @Test 130 | public void exampleJSTest() throws IOException { 131 | // This test only makes sense if JS is available. 132 | Assume.assumeTrue(Engine.create().getLanguages().containsKey("js")); 133 | // This is how we can create a context with our tool enabled if we are embeddined in java 134 | try (Context context = Context.newBuilder("js").option(SimpleCoverageInstrument.ID, "true").option(SimpleCoverageInstrument.ID + ".PrintCoverage", "false").build()) { 135 | Source source = Source.newBuilder("js", JS_SOURCE, "main").build(); 136 | context.eval(source); 137 | assertJSCorrect(context); 138 | } 139 | } 140 | 141 | // NOTE: This lookup mechanism used in this method does not work in normal deployments 142 | // due to Truffles class path issolation. Services can be looked up by other 143 | // instruments, but not by the embedder. We can do this in the tests because the 144 | // class path issolation is disabled in the pom.xml file by adding -XX:-UseJVMCIClassLoader to 145 | // the command line. 146 | // This command line flag should never be used in production. 147 | private static void assertJSCorrect(final Context context) { 148 | // We can lookup services exported by the instrument, in our case this is 149 | // the instrument itself but it does not have to be. 150 | SimpleCoverageInstrument coverageInstrument = context.getEngine().getInstruments().get(SimpleCoverageInstrument.ID).lookup(SimpleCoverageInstrument.class); 151 | // We then use the looked up service to assert that it behaves as expected, just like in any 152 | // other test. 153 | Map coverageMap = coverageInstrument.getCoverageMap(); 154 | Assert.assertEquals(1, coverageMap.size()); 155 | coverageMap.forEach((com.oracle.truffle.api.source.Source s, Coverage v) -> { 156 | Set notYetCoveredLineNumbers = coverageInstrument.nonCoveredLineNumbers(s); 157 | Object[] expected = new Integer[]{47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 61, 67}; 158 | Assert.assertArrayEquals(expected, notYetCoveredLineNumbers.stream().sorted().toArray()); 159 | }); 160 | } 161 | 162 | private static final String SL_SOURCE = "\n" + 163 | "function neverCalled() {\n" + 164 | " x = 5;\n" + 165 | " y = 9;\n" + 166 | " return x + 5;\n" + 167 | "}\n" + 168 | "function isCalled() {\n" + 169 | " return 5 + 5;\n" + 170 | "}\n" + 171 | "function main() { \n" + 172 | " 10 + isCalled(); \n" + 173 | "}\n"; 174 | 175 | // Similar test using simple language 176 | @Test 177 | public void exampleSLTest() throws IOException { 178 | // This test only makes sense if SL is available. 179 | Assume.assumeTrue(Engine.create().getLanguages().containsKey("sl")); 180 | // This is how we can create a context with our tool enabled if we are embeddined in java 181 | try (Context context = Context.newBuilder("sl").option(SimpleCoverageInstrument.ID, "true").option(SimpleCoverageInstrument.ID + ".PrintCoverage", "false").build()) { 182 | Source source = Source.newBuilder("sl", SL_SOURCE, "main").build(); 183 | context.eval(source); 184 | // We can lookup services exported by the instrument, in our case this is 185 | // the instrument itself but it does not have to be. 186 | SimpleCoverageInstrument coverageInstrument = context.getEngine().getInstruments().get(SimpleCoverageInstrument.ID).lookup(SimpleCoverageInstrument.class); 187 | // We then use the looked up service to assert that it behaves as expected, just like in 188 | // any other test. 189 | Map coverageMap = coverageInstrument.getCoverageMap(); 190 | Assert.assertEquals(1, coverageMap.size()); 191 | coverageMap.forEach((com.oracle.truffle.api.source.Source s, Coverage v) -> { 192 | Set notYetCoveredLineNumbers = coverageInstrument.nonCoveredLineNumbers(s); 193 | Object[] expected = new Integer[]{3, 4, 5}; 194 | Assert.assertArrayEquals(expected, notYetCoveredLineNumbers.stream().sorted().toArray()); 195 | }); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * The Universal Permissive License (UPL), Version 1.0 6 | * 7 | * Subject to the condition set forth below, permission is hereby granted to any 8 | * person obtaining a copy of this software, associated documentation and/or 9 | * data (collectively the "Software"), free of charge and under any and all 10 | * copyright rights in the Software, and any and all patent rights owned or 11 | * freely licensable by each licensor hereunder covering either (i) the 12 | * unmodified Software as contributed to or provided by such licensor, or (ii) 13 | * the Larger Works (as defined below), to deal in both 14 | * 15 | * (a) the Software, and 16 | * 17 | * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 18 | * one is included with the Software each a "Larger Work" to which the Software 19 | * is contributed by such licensors), 20 | * 21 | * without restriction, including without limitation the rights to copy, create 22 | * derivative works of, display, perform, and distribute the Software and make, 23 | * use, sell, offer for sale, import, export, have made, and have sold the 24 | * Software and the Larger Work(s), and to sublicense the foregoing rights on 25 | * either these or other terms. 26 | * 27 | * This license is subject to the following condition: 28 | * 29 | * The above copyright notice and either this complete permission notice or at a 30 | * minimum a reference to the UPL must be included in all copies or substantial 31 | * portions of the Software. 32 | * 33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | * SOFTWARE. 40 | */ 41 | open module org.graalvm.st.test { 42 | requires java.logging; 43 | requires jdk.unsupported; 44 | requires org.graalvm.polyglot; 45 | requires junit; 46 | requires org.graalvm.truffle; 47 | requires org.graalvm.st; 48 | exports com.oracle.truffle.st.test; 49 | 50 | } 51 | --------------------------------------------------------------------------------