1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-07-13 07:22:52 +00:00
Jonathan Coates 9b3cadf57c
Cherry pick several changes back from 1.19.3
The main purpose of this is to backport the improved parse/runtime
errors to older versions. I think they're sufficiently useful that we
should try to make it as widely available as possible.

We've been running them for a week now on SC3 and the released version
and not seen any issues, so I think it's probably stable enough.

This is a pretty lazy commit: I ended up copying the whole ROM over and
then picking up a few other related changes along the way.

 - Trim spaces from file paths (b8fce1eecccae652d1128fcf50b57a09eda69dca)

 - Correctly format 12AM/PM with
   %I (9f48395596131a932fbc37644fe1e4b15ffb6a61)

 - Fix http.request and htpt.websocketAsync not handling a few failure
   edge-cases correctly (3b42f22a4f36dad0c53bb238e64aada352a063cf).

 - Move the internal modules into the main package path, hidden under
   cc.internal (34a31abd9ce9106b84549ade2cc30524016107c9).

 - Gather code coverage in Java instead of
   Lua (28a55349a961c0739adc9d52fc3761c463678be9).

 - Make error messages in edit more
   obvious (8cfbfe7ceb35e87579b4f6fe8c892e6bce9ed0eb).

 - Make mcfly's test methods global. This means we don't need to pass
   stub everywhere (7335a892b5742f7879a4ca07f059cd7b8136aa3a).

 - Improve runtime and parse errors. This comes from numerous commits,
   but chiefly a12b405acfb63d58d6a895e8a8a139ef5c42fbfc, and
   55024121817bb112ea68d30e7cb5511a16ccfc94.

 - Hide the internal redirect methods in
   multishell (33b6f383397d51074bd504a4067253ae65f5b77c).

Note this does /not/ include the shebang changes (sorry Emma!). I've
tried to avoid adding any user-controllable features, mostly because I
don't know how to handle the versioning otherwise :).
2023-02-14 09:45:03 +00:00

157 lines
5.0 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core;
import com.google.common.base.Strings;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntMaps;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squiddev.cobalt.Prototype;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LuaC;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Map;
import java.util.Queue;
class LuaCoverage
{
private static final Logger LOG = LoggerFactory.getLogger( LuaCoverage.class );
private static final Path ROOT = new File( "src/main/resources/data/computercraft/lua" ).toPath();
private final Map<String, Int2IntMap> coverage;
private final String blank;
private final String zero;
private final String countFormat;
LuaCoverage( Map<String, Int2IntMap> coverage )
{
this.coverage = coverage;
int max = coverage.values().stream()
.flatMapToInt( x -> x.values().stream().mapToInt( y -> y ) )
.max().orElse( 0 );
int maxLen = Math.max( 1, (int) Math.ceil( Math.log10( max ) ) );
blank = Strings.repeat( " ", maxLen + 1 );
zero = Strings.repeat( "*", maxLen ) + "0";
countFormat = "%" + (maxLen + 1) + "d";
}
void write( Writer out ) throws IOException
{
Files.find( ROOT, Integer.MAX_VALUE, ( path, attr ) -> attr.isRegularFile() ).forEach( path -> {
Path relative = ROOT.relativize( path );
String full = relative.toString().replace( '\\', '/' );
if( !full.endsWith( ".lua" ) ) return;
Int2IntMap possiblePaths = coverage.remove( "/" + full );
if( possiblePaths == null ) possiblePaths = coverage.remove( full );
if( possiblePaths == null )
{
possiblePaths = Int2IntMaps.EMPTY_MAP;
LOG.warn( "{} has no coverage data", full );
}
try
{
writeCoverageFor( out, path, possiblePaths );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
} );
for( String filename : coverage.keySet() )
{
if( filename.startsWith( "/test-rom/" ) ) continue;
LOG.warn( "Unknown file {}", filename );
}
}
private void writeCoverageFor( Writer out, Path fullName, Int2IntMap visitedLines ) throws IOException
{
if( !Files.exists( fullName ) )
{
LOG.error( "Cannot locate file {}", fullName );
return;
}
IntSet activeLines = getActiveLines( fullName.toFile() );
out.write( "==============================================================================\n" );
out.write( fullName.toString().replace( '\\', '/' ) );
out.write( "\n" );
out.write( "==============================================================================\n" );
try( BufferedReader reader = Files.newBufferedReader( fullName ) )
{
String line;
int lineNo = 0;
while( (line = reader.readLine()) != null )
{
lineNo++;
int count = visitedLines.getOrDefault( lineNo, -1 );
if( count >= 0 )
{
out.write( String.format( countFormat, count ) );
}
else if( activeLines.contains( lineNo ) )
{
out.write( zero );
}
else
{
out.write( blank );
}
out.write( ' ' );
out.write( line );
out.write( "\n" );
}
}
}
private static IntSet getActiveLines( File file ) throws IOException
{
IntSet activeLines = new IntOpenHashSet();
Queue<Prototype> queue = new ArrayDeque<>();
try( InputStream stream = Files.newInputStream( file.toPath() ) )
{
Prototype proto = LuaC.compile( stream, "@" + file.getPath() );
queue.add( proto );
}
catch( CompileException e )
{
throw new IllegalStateException( "Cannot compile", e );
}
Prototype proto;
while( (proto = queue.poll()) != null )
{
int[] lines = proto.lineInfo;
if( lines != null )
{
for( int line : lines )
{
activeLines.add( line );
}
}
if( proto.children != null ) Collections.addAll( queue, proto.children );
}
return activeLines;
}
}