From 3365da9712ba0dd4b0fd66fb30d53316195261f7 Mon Sep 17 00:00:00 2001 From: tdurieux Date: Tue, 7 Mar 2017 13:22:42 +0100 Subject: [PATCH] buggy files form Closure #47 --- .../sourcemap/SourceMapConsumerV3.java | 724 ++++++++++++++++++ .../google/javascript/jscomp/SourceMap.java | 204 +++++ 2 files changed, 928 insertions(+) create mode 100644 projects/Closure/47/com/google/debugging/sourcemap/SourceMapConsumerV3.java create mode 100644 projects/Closure/47/com/google/javascript/jscomp/SourceMap.java diff --git a/projects/Closure/47/com/google/debugging/sourcemap/SourceMapConsumerV3.java b/projects/Closure/47/com/google/debugging/sourcemap/SourceMapConsumerV3.java new file mode 100644 index 0000000..d67dc10 --- /dev/null +++ b/projects/Closure/47/com/google/debugging/sourcemap/SourceMapConsumerV3.java @@ -0,0 +1,724 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.debugging.sourcemap.Base64VLQ.CharIterator; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Class for parsing version 3 of the SourceMap format, as produced by the + * Closure Compiler, etc. + * http://code.google.com/p/closure-compiler/wiki/SourceMaps + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapConsumerV3 implements SourceMapConsumer, + SourceMappingReversable { + static final int UNMAPPED = -1; + + private String[] sources; + private String[] names; + private int lineCount; + // Slots in the lines list will be null if the line does not have any entries. + private ArrayList> lines = null; + /** originalFile path ==> original line ==> target mappings */ + private Map>> + reverseSourceMapping; + + public SourceMapConsumerV3() { + + } + + static class DefaultSourceMapSupplier implements SourceMapSupplier { + @Override + public String getSourceMap(String url) { + return null; + } + } + + /** + * Parses the given contents containing a source map. + */ + @Override + public void parse(String contents) throws SourceMapParseException { + parse(contents, null); + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(String contents, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + try { + JSONObject sourceMapRoot = new JSONObject(contents); + parse(sourceMapRoot, sectionSupplier); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(JSONObject sourceMapRoot) throws SourceMapParseException { + parse(sourceMapRoot, null); + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + try { + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + if (version != 3) { + throw new SourceMapParseException("Unknown version: " + version); + } + + String file = sourceMapRoot.getString("file"); + if (file.isEmpty()) { + throw new SourceMapParseException("File entry is missing or empty"); + } + + if (sourceMapRoot.has("sections")) { + // Looks like a index map, try to parse it that way. + parseMetaMap(sourceMapRoot, sectionSupplier); + return; + } + + lineCount = sourceMapRoot.getInt("lineCount"); + String lineMap = sourceMapRoot.getString("mappings"); + + sources = getJavaStringArray(sourceMapRoot.getJSONArray("sources")); + names = getJavaStringArray(sourceMapRoot.getJSONArray("names")); + + lines = Lists.newArrayListWithCapacity(lineCount); + + new MappingBuilder(lineMap).build(); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * @param sourceMapRoot + * @throws SourceMapParseException + */ + private void parseMetaMap( + JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + if (sectionSupplier == null) { + sectionSupplier = new DefaultSourceMapSupplier(); + } + + try { + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + if (version != 3) { + throw new SourceMapParseException("Unknown version: " + version); + } + + String file = sourceMapRoot.getString("file"); + if (file.isEmpty()) { + throw new SourceMapParseException("File entry is missing or empty"); + } + + if (sourceMapRoot.has("lineCount") + || sourceMapRoot.has("mappings") + || sourceMapRoot.has("sources") + || sourceMapRoot.has("names")) { + throw new SourceMapParseException("Invalid map format"); + } + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + JSONArray sections = sourceMapRoot.getJSONArray("sections"); + for (int i = 0, count = sections.length(); i < count; i++) { + JSONObject section = sections.getJSONObject(i); + if (section.has("map") && section.has("url")) { + throw new SourceMapParseException( + "Invalid map format: section may not have both 'map' and 'url'"); + } + JSONObject offset = section.getJSONObject("offset"); + int line = offset.getInt("line"); + int column = offset.getInt("column"); + String mapSectionContents; + if (section.has("url")) { + String url = section.getString("url"); + mapSectionContents = sectionSupplier.getSourceMap(url); + if (mapSectionContents == null) { + throw new SourceMapParseException("Unable to retrieve: " + url); + } + } else if (section.has("map")) { + mapSectionContents = section.getString("map"); + } else { + throw new SourceMapParseException( + "Invalid map format: section must have either 'map' or 'url'"); + } + generator.mergeMapSection(line, column, mapSectionContents); + } + + StringBuilder sb = new StringBuilder(); + try { + generator.appendTo(sb, file); + } catch (IOException e) { + // Can't happen. + throw new RuntimeException(e); + } + + parse(sb.toString()); + } catch (IOException ex) { + throw new SourceMapParseException("IO exception: " + ex); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + @Override + public OriginalMapping getMappingForLine(int lineNumber, int column) { + // Normalize the line and column numbers to 0. + lineNumber--; + column--; + + if (lineNumber < 0 || lineNumber >= lines.size()) { + return null; + } + + Preconditions.checkState(lineNumber >= 0); + Preconditions.checkState(column >= 0); + + + // If the line is empty return the previous mapping. + if (lines.get(lineNumber) == null) { + return getPreviousMapping(lineNumber); + } + + ArrayList entries = lines.get(lineNumber); + // No empty lists. + Preconditions.checkState(entries.size() > 0); + if (entries.get(0).getGeneratedColumn() > column) { + return getPreviousMapping(lineNumber); + } + + int index = search(entries, column, 0, entries.size() - 1); + Preconditions.checkState(index >= 0, "unexpected:%s", index); + return getOriginalMappingForEntry(entries.get(index)); + } + + @Override + public Collection getOriginalSources() { + return Arrays.asList(sources); + } + + @Override + public Collection getReverseMapping(String originalFile, + int line, int column) { + // TODO(user): This implementation currently does not make use of the column + // parameter. + + // Synchronization needs to be handled by callers. + if (reverseSourceMapping == null) { + createReverseMapping(); + } + + Map> sourceLineToCollectionMap = + reverseSourceMapping.get(originalFile); + + if (sourceLineToCollectionMap == null) { + return Collections.emptyList(); + } else { + Collection mappings = + sourceLineToCollectionMap.get(line); + + if (mappings == null) { + return Collections.emptyList(); + } else { + return mappings; + } + } + } + + private String[] getJavaStringArray(JSONArray array) throws JSONException { + int len = array.length(); + String[] result = new String[len]; + for(int i = 0; i < len; i++) { + result[i] = array.getString(i); + } + return result; + } + + private class MappingBuilder { + private static final int MAX_ENTRY_VALUES = 5; + private final StringCharIterator content; + private int line = 0; + private int previousCol = 0; + private int previousSrcId = 0; + private int previousSrcLine = 0; + private int previousSrcColumn = 0; + private int previousNameId = 0; + + MappingBuilder(String lineMap) { + this.content = new StringCharIterator(lineMap); + } + + void build() { + int [] temp = new int[MAX_ENTRY_VALUES]; + ArrayList entries = new ArrayList(); + while (content.hasNext()) { + // ';' denotes a new line. + if (tryConsumeToken(';')) { + // The line is complete, store the result for the line, + // null if the line is empty. + ArrayList result; + if (entries.size() > 0) { + result = entries; + // A new array list for the next line. + entries = new ArrayList(); + } else { + result = null; + } + lines.add(result); + entries.clear(); + line++; + previousCol = 0; + } else { + // grab the next entry for the current line. + int entryValues = 0; + while (!entryComplete()) { + temp[entryValues] = nextValue(); + entryValues++; + } + Entry entry = decodeEntry(temp, entryValues); + + validateEntry(entry); + entries.add(entry); + + // Consume the separating token, if there is one. + tryConsumeToken(','); + } + } + } + + /** + * Sanity check the entry. + */ + private void validateEntry(Entry entry) { + Preconditions.checkState(line < lineCount); + Preconditions.checkState(entry.getSourceFileId() == UNMAPPED + || entry.getSourceFileId() < sources.length); + Preconditions.checkState(entry.getNameId() == UNMAPPED + || entry.getNameId() < names.length); + } + + /** + * Decodes the next entry, using the previous encountered values to + * decode the relative values. + * + * @param vals An array of integers that represent values in the entry. + * @param entryValues The number of entries in the array. + * @return The entry object. + */ + private Entry decodeEntry(int[] vals, int entryValues) { + Entry entry; + switch (entryValues) { + // The first values, if present are in the following order: + // 0: the starting column in the current line of the generated file + // 1: the id of the original source file + // 2: the starting line in the original source + // 3: the starting column in the original source + // 4: the id of the original symbol name + // The values are relative to the last encountered value for that field. + // Note: the previously column value for the generated file is reset + // to '0' when a new line is encountered. This is done in the 'build' + // method. + + case 1: + // An unmapped section of the generated file. + entry = new UnmappedEntry( + vals[0] + previousCol); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + return entry; + + case 4: + // A mapped section of the generated file. + entry = new UnnamedEntry( + vals[0] + previousCol, + vals[1] + previousSrcId, + vals[2] + previousSrcLine, + vals[3] + previousSrcColumn); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + previousSrcId = entry.getSourceFileId(); + previousSrcLine = entry.getSourceLine(); + previousSrcColumn = entry.getSourceColumn(); + return entry; + + case 5: + // A mapped section of the generated file, that has an associated + // name. + entry = new NamedEntry( + vals[0] + previousCol, + vals[1] + previousSrcId, + vals[2] + previousSrcLine, + vals[3] + previousSrcColumn, + vals[4] + previousNameId); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + previousSrcId = entry.getSourceFileId(); + previousSrcLine = entry.getSourceLine(); + previousSrcColumn = entry.getSourceColumn(); + previousNameId = entry.getNameId(); + return entry; + + default: + throw new IllegalStateException( + "Unexpected number of values for entry:" + entryValues); + } + } + + private boolean tryConsumeToken(char token) { + if (content.hasNext() && content.peek() == token) { + // consume the comma + content.next(); + return true; + } + return false; + } + + private boolean entryComplete() { + if (!content.hasNext()) { + return true; + } + + char c = content.peek(); + return (c == ';' || c == ','); + } + + private int nextValue() { + return Base64VLQ.decode(content); + } + } + + /** + * Perform a binary search on the array to find a section that covers + * the target column. + */ + private int search(ArrayList entries, int target, int start, int end) { + while (true) { + int mid = ((end - start) / 2) + start; + int compare = compareEntry(entries, mid, target); + if (compare == 0) { + return mid; + } else if (compare < 0) { + // it is in the upper half + start = mid + 1; + if (start > end) { + return end; + } + } else { + // it is in the lower half + end = mid - 1; + if (end < start) { + return end; + } + } + } + } + + /** + * Compare an array entry's column value to the taget column value. + */ + private int compareEntry(ArrayList entries, int entry, int target) { + return entries.get(entry).getGeneratedColumn() - target; + } + + /** + * Returns the mapping entry that proceeds the supplied line or null if no + * such entry exists. + */ + private OriginalMapping getPreviousMapping(int lineNumber) { + do { + if (lineNumber == 0) { + return null; + } + lineNumber--; + } while (lines.get(lineNumber) == null); + ArrayList entries = lines.get(lineNumber); + return getOriginalMappingForEntry(entries.get(entries.size() - 1)); + } + + /** + * Creates an "OriginalMapping" object for the given entry object. + */ + private OriginalMapping getOriginalMappingForEntry(Entry entry) { + if (entry.getSourceFileId() == UNMAPPED) { + return null; + } else { + // Adjust the line/column here to be start at 1. + Builder x = OriginalMapping.newBuilder() + .setOriginalFile(sources[entry.getSourceFileId()]) + .setLineNumber(entry.getSourceLine()) + .setColumnPosition(entry.getSourceColumn()); + if (entry.getNameId() != UNMAPPED) { + x.setIdentifier(names[entry.getNameId()]); + } + return x.build(); + } + } + + /** + * Reverse the source map; the created mapping will allow us to quickly go + * from a source file and line number to a collection of target + * OriginalMappings. + */ + private void createReverseMapping() { + reverseSourceMapping = + new HashMap>>(); + + for (int targetLine = 0; targetLine < lines.size(); targetLine++) { + ArrayList entries = lines.get(targetLine); + + if (entries != null) { + for (Entry entry : entries) { + if (entry.getSourceFileId() != UNMAPPED + && entry.getSourceLine() != UNMAPPED) { + String originalFile = sources[entry.getSourceFileId()]; + + if (!reverseSourceMapping.containsKey(originalFile)) { + reverseSourceMapping.put(originalFile, + new HashMap>()); + } + + Map> lineToCollectionMap = + reverseSourceMapping.get(originalFile); + + int sourceLine = entry.getSourceLine(); + + if (!lineToCollectionMap.containsKey(sourceLine)) { + lineToCollectionMap.put(sourceLine, + new ArrayList(1)); + } + + Collection mappings = + lineToCollectionMap.get(sourceLine); + + Builder builder = OriginalMapping.newBuilder().setLineNumber( + targetLine).setColumnPosition(entry.getGeneratedColumn()); + + mappings.add(builder.build()); + } + } + } + } + } + + /** + * A implementation of the Base64VLQ CharIterator used for decoding the + * mappings encoded in the JSON string. + */ + private static class StringCharIterator implements CharIterator { + final String content; + final int length; + int current = 0; + + StringCharIterator(String content) { + this.content = content; + this.length = content.length(); + } + + @Override + public char next() { + return content.charAt(current++); + } + + char peek() { + return content.charAt(current); + } + + @Override + public boolean hasNext() { + return current < length; + } + } + + /** + * Represents a mapping entry in the source map. + */ + private interface Entry { + int getGeneratedColumn(); + int getSourceFileId(); + int getSourceLine(); + int getSourceColumn(); + int getNameId(); + } + + /** + * This class represents a portion of the generated file, that is not mapped + * to a section in the original source. + */ + private static class UnmappedEntry implements Entry { + private final int column; + + UnmappedEntry(int column) { + this.column = column; + } + + @Override + public int getGeneratedColumn() { + return column; + } + + @Override + public int getSourceFileId() { + return UNMAPPED; + } + + @Override + public int getSourceLine() { + return UNMAPPED; + } + + @Override + public int getSourceColumn() { + return UNMAPPED; + } + + @Override + public int getNameId() { + return UNMAPPED; + } + } + + /** + * This class represents a portion of the generated file, that is mapped + * to a section in the original source. + */ + private static class UnnamedEntry extends UnmappedEntry { + private final int srcFile; + private final int srcLine; + private final int srcColumn; + + UnnamedEntry(int column, int srcFile, int srcLine, int srcColumn) { + super(column); + this.srcFile = srcFile; + this.srcLine = srcLine; + this.srcColumn = srcColumn; + } + + @Override + public int getSourceFileId() { + return srcFile; + } + + @Override + public int getSourceLine() { + return srcLine; + } + + @Override + public int getSourceColumn() { + return srcColumn; + } + + @Override + public int getNameId() { + return UNMAPPED; + } + } + + /** + * This class represents a portion of the generated file, that is mapped + * to a section in the original source, and is associated with a name. + */ + private static class NamedEntry extends UnnamedEntry { + private final int name; + + NamedEntry(int column, int srcFile, int srcLine, int srcColumn, int name) { + super(column, srcFile, srcLine, srcColumn); + this.name = name; + } + + @Override + public int getNameId() { + return name; + } + } + + static interface EntryVisitor { + void visit(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition); + } + + public void visitMappings(EntryVisitor visitor) { + boolean pending = false; + String sourceName = null; + String symbolName = null; + FilePosition sourceStartPosition = null; + FilePosition startPosition = null; + + final int lineCount = lines.size(); + for (int i = 0; i < lineCount; i++) { + ArrayList line = lines.get(i); + if (line != null) { + final int entryCount = line.size(); + for (int j = 0; j < entryCount; j++) { + Entry entry = line.get(j); + if (pending) { + FilePosition endPosition = new FilePosition( + i, entry.getGeneratedColumn()); + visitor.visit( + sourceName, + symbolName, + sourceStartPosition, + startPosition, + endPosition); + pending = false; + } + + if (entry.getSourceFileId() != UNMAPPED) { + pending = true; + sourceName = sources[entry.getSourceFileId()]; + symbolName = (entry.getNameId() != UNMAPPED) + ? names[entry.getNameId()] : null; + sourceStartPosition = new FilePosition( + entry.getSourceLine(), entry.getSourceColumn()); + startPosition = new FilePosition( + i, entry.getGeneratedColumn()); + } + } + } + } + } +} diff --git a/projects/Closure/47/com/google/javascript/jscomp/SourceMap.java b/projects/Closure/47/com/google/javascript/jscomp/SourceMap.java new file mode 100644 index 0000000..835dfa4 --- /dev/null +++ b/projects/Closure/47/com/google/javascript/jscomp/SourceMap.java @@ -0,0 +1,204 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.FilePosition; +import com.google.debugging.sourcemap.SourceMapFormat; +import com.google.debugging.sourcemap.SourceMapGenerator; +import com.google.debugging.sourcemap.SourceMapGeneratorFactory; +import com.google.debugging.sourcemap.SourceMapGeneratorV1; +import com.google.debugging.sourcemap.SourceMapGeneratorV2; +import com.google.javascript.rhino.Node; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + * @see CodeConsumer + * @see CodeGenerator + * @see CodePrinter + * + */ +public class SourceMap { + + public static enum Format { + V1 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V1)); + } + }, + DEFAULT { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.DEFAULT)); + } + }, + V2 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V2)); + } + }, + V3 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3)); + } + }; + abstract SourceMap getInstance(); + } + + /** + * Source maps can be very large different levels of detail can be specified. + */ + public static enum DetailLevel implements Predicate { + // ALL is best when the fullest details are needed for debugging or for + // code-origin analysis. + ALL { + @Override public boolean apply(Node node) { + return true; + } + }, + // SYMBOLS is intended to be used for stack trace deobfuscation when full + // detail is not needed. + SYMBOLS { + @Override public boolean apply(Node node) { + return node.isCall() + || node.isNew() + || node.isFunction() + || node.isName() + || NodeUtil.isGet(node) + || NodeUtil.isObjectLitKey(node, node.getParent()) + || (node.isString() && NodeUtil.isGet(node.getParent())); + } + }; + } + + public static class LocationMapping { + final String prefix; + final String replacement; + public LocationMapping(String prefix, String replacement) { + this.prefix = prefix; + this.replacement = replacement; + } + } + + private final SourceMapGenerator generator; + private List prefixMappings = Collections.emptyList(); + private final Map sourceLocationFixupCache = + Maps.newHashMap(); + + private SourceMap(SourceMapGenerator generator) { + this.generator = generator; + } + + public void addMapping( + Node node, + FilePosition outputStartPosition, + FilePosition outputEndPosition) { + String sourceFile = node.getSourceFileName(); + + // If the node does not have an associated source file or + // its line number is -1, then the node does not have sufficient + // information for a mapping to be useful. + if (sourceFile == null || node.getLineno() < 0) { + return; + } + + sourceFile = fixupSourceLocation(sourceFile); + + String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP); + + // Strangely, Rhino source lines are one based but columns are + // zero based. + // We don't change this for the v1 or v2 source maps but for + // v3 we make them both 0 based. + + generator.addMapping( + sourceFile, originalName, + new FilePosition(node.getLineno(), node.getCharno()), + outputStartPosition, outputEndPosition); + } + + /** + * @param sourceFile The source file location to fixup. + * @return a remapped source file. + */ + private String fixupSourceLocation(String sourceFile) { + if (prefixMappings.isEmpty()) { + return sourceFile; + } + + String fixed = sourceLocationFixupCache.get(sourceFile); + if (fixed != null) { + return fixed; + } + + // Replace the first prefix found with its replacement + for (LocationMapping mapping : prefixMappings) { + if (sourceFile.startsWith(mapping.prefix)) { + fixed = mapping.replacement + sourceFile.substring( + mapping.prefix.length()); + break; + } + } + + // If none of the mappings match then use the original file path. + if (fixed == null) { + fixed = sourceFile; + } + + sourceLocationFixupCache.put(sourceFile, fixed); + return fixed; + } + + public void appendTo(Appendable out, String name) throws IOException { + generator.appendTo(out, name); + } + + public void reset() { + generator.reset(); + sourceLocationFixupCache.clear(); + } + + public void setStartingPosition(int offsetLine, int offsetIndex) { + generator.setStartingPosition(offsetLine, offsetIndex); + } + + public void setWrapperPrefix(String prefix) { + generator.setWrapperPrefix(prefix); + } + + public void validate(boolean validate) { + generator.validate(validate); + } + + /** + * @param sourceMapLocationMappings + */ + public void setPrefixMappings(List sourceMapLocationMappings) { + this.prefixMappings = sourceMapLocationMappings; + } +}