package com.oracle.truffle.js.runtime.objects;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.UserScriptException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class DefaultESModuleLoader implements JSModuleLoader {
protected final JSRealm realm;
protected final Map<String, JSModuleRecord> moduleMap = new HashMap<>();
public static DefaultESModuleLoader create(JSRealm realm) {
return new DefaultESModuleLoader(realm);
}
protected DefaultESModuleLoader(JSRealm realm) {
this.realm = realm;
}
protected URI asURI(String specifier) {
if (specifier.indexOf(':') == -1) {
return null;
}
try {
URI uri = new URI(specifier);
return uri.getScheme() != null ? uri : null;
} catch (URISyntaxException e) {
return null;
}
}
@Override
public JSModuleRecord resolveImportedModule(ScriptOrModule referrer, String specifier) {
String refPath = referrer == null ? null : referrer.getSource().getPath();
try {
TruffleFile moduleFile;
URI maybeUri = asURI(specifier);
if (refPath == null) {
if (maybeUri != null) {
moduleFile = realm.getEnv().getPublicTruffleFile(maybeUri).getCanonicalFile();
} else {
moduleFile = realm.getEnv().getPublicTruffleFile(specifier).getCanonicalFile();
}
} else {
TruffleFile refFile = realm.getEnv().getPublicTruffleFile(refPath);
if (maybeUri != null) {
String uriFile = realm.getEnv().getPublicTruffleFile(maybeUri).getCanonicalFile().getPath();
moduleFile = refFile.resolveSibling(uriFile).getCanonicalFile();
} else {
moduleFile = refFile.resolveSibling(specifier).getCanonicalFile();
}
}
String canonicalPath = moduleFile.getPath();
return loadModuleFromUrl(specifier, moduleFile, canonicalPath);
} catch (FileSystemException fsex) {
String fileName = fsex.getFile();
if (Objects.equals(fsex.getMessage(), fileName)) {
String message = "Error reading: " + fileName;
if (realm.getContext().isOptionV8CompatibilityMode()) {
throw UserScriptException.create(message);
} else {
throw Errors.createError(message);
}
} else {
throw Errors.createErrorFromException(fsex);
}
} catch (IOException | SecurityException e) {
throw Errors.createErrorFromException(e);
}
}
protected JSModuleRecord loadModuleFromUrl(String specifier, TruffleFile moduleFile, String canonicalPath) throws IOException {
JSModuleRecord existingModule = moduleMap.get(canonicalPath);
if (existingModule != null) {
return existingModule;
}
Source source = Source.newBuilder(JavaScriptLanguage.ID, moduleFile).name(specifier).build();
JSModuleRecord newModule = realm.getContext().getEvaluator().parseModule(realm.getContext(), source, this);
moduleMap.put(canonicalPath, newModule);
return newModule;
}
@Override
public JSModuleRecord loadModule(Source source) {
String path = source.getPath();
String canonicalPath;
if (path == null) {
canonicalPath = source.getName();
} else {
try {
TruffleFile moduleFile = realm.getEnv().getPublicTruffleFile(path);
if (moduleFile.exists()) {
canonicalPath = moduleFile.getCanonicalFile().getPath();
} else {
canonicalPath = path;
}
} catch (IOException | SecurityException e) {
throw Errors.createErrorFromException(e);
}
}
return moduleMap.computeIfAbsent(canonicalPath, (key) -> realm.getContext().getEvaluator().parseModule(realm.getContext(), source, this));
}
}