/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.internal; import static com.github.anba.es6draft.runtime.modules.ModuleSemantics.GetModuleNamespace; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import com.github.anba.es6draft.Script; import com.github.anba.es6draft.compiler.CompilationException; import com.github.anba.es6draft.parser.Parser; import com.github.anba.es6draft.parser.ParserException; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.modules.MalformedNameException; import com.github.anba.es6draft.runtime.modules.ModuleExport; import com.github.anba.es6draft.runtime.modules.ModuleRecord; import com.github.anba.es6draft.runtime.modules.ResolutionException; import com.github.anba.es6draft.runtime.modules.SourceTextModuleRecord; import com.github.anba.es6draft.runtime.modules.loader.URLModuleLoader; import com.github.anba.es6draft.runtime.modules.loader.URLModuleSource; import com.github.anba.es6draft.runtime.modules.loader.URLSourceIdentifier; import com.github.anba.es6draft.runtime.types.ScriptObject; /** * */ // TODO: Rename 'NativeCode' public final class NativeCode { private NativeCode() { } /** * Returns the URL for the script {@code name} from the 'scripts' directory. * * @param name * the script name * @return the script's URL * @throws IOException * if the resource could not be found */ public static URL getScriptURL(String name) throws IOException { String sourceName = "/scripts/" + name; URL url = NativeCode.class.getResource(sourceName); if (url == null) { throw new IOException(String.format("script '%s' not found", name)); } return url; } /** * Parses, compiles and executes the javascript file. (Uses the script cache.) * <p> * The script file is loaded as a native script with elevated privileges. * * @param realm * the realm instance * @param name * the script name * @throws IOException * if there was any I/O error * @throws URISyntaxException * the URL is not a valid URI * @throws ParserException * if the source contains any syntax errors * @throws CompilationException * if the parsed source could not be compiled */ public static void load(Realm realm, String name) throws IOException, URISyntaxException, ParserException, CompilationException { RuntimeContext context = realm.getWorld().getContext(); ScriptCache scriptCache = context.getScriptCache(); Script script = scriptCache.get(createNativeScriptLoader(context), getScriptURL(name)); script.evaluate(realm); } /** * Loads the javascript module file. * <p> * The script file is loaded as a native module with elevated privileges. * * @param realm * the realm instance * @param name * the module name * @return the native module record * @throws IOException * if there was any I/O error * @throws URISyntaxException * the URL is not a valid URI * @throws MalformedNameException * if any imported module request cannot be normalized * @throws ResolutionException * if any export binding cannot be resolved * @throws ParserException * if the module source contains any syntax errors * @throws CompilationException * if the parsed module source cannot be compiled */ public static ModuleRecord loadModule(Realm realm, String name) throws IOException, URISyntaxException, MalformedNameException, ResolutionException { RuntimeContext context = realm.getWorld().getContext(); URLModuleLoader urlLoader = new URLModuleLoader(context, createNativeScriptLoader(context)); URLSourceIdentifier sourceId = new URLSourceIdentifier(getScriptURL(name)); URLModuleSource source = new URLModuleSource(sourceId); SourceTextModuleRecord module = urlLoader.define(sourceId, source, realm); module.instantiate(); module.evaluate(); return module; } /** * Resolves and returns the exported binding from a module record. * * @param <T> * the object type * @param module * the module record * @param exportName * the export name * @param clazz * the expected class * @return the exported value * @throws IOException * if there was any I/O error * @throws MalformedNameException * if any imported module request cannot be normalized * @throws ResolutionException * if any export binding cannot be resolved */ public static <T> T getModuleExport(ModuleRecord module, String exportName, Class<T> clazz) throws IOException, MalformedNameException, ResolutionException { ModuleExport export = module.resolveExport(exportName, new HashMap<>(), new HashSet<>()); if (export == null) { throw new ResolutionException(Messages.Key.ModulesUnresolvedExport, exportName); } if (export.isAmbiguous()) { throw new ResolutionException(Messages.Key.ModulesAmbiguousExport, exportName); } ModuleRecord targetModule = export.getModule(); if (!targetModule.isInstantiated() || !targetModule.isEvaluated()) { throw new IllegalStateException(); } if (export.isNameSpaceExport()) { Realm realm = module.getRealm(); if (realm == null) { throw new IllegalArgumentException(); } ScriptObject namespace = GetModuleNamespace(realm.defaultContext(), targetModule); return clazz.cast(namespace); } LexicalEnvironment<?> targetEnv = targetModule.getEnvironment(); if (targetEnv == null) { throw new ResolutionException(Messages.Key.UninitializedModuleBinding, export.getBindingName(), targetModule.getSourceCodeId().toString()); } Object bindingValue = targetEnv.getEnvRec().getBindingValue(export.getBindingName(), true); return clazz.cast(bindingValue); } private static ScriptLoader createNativeScriptLoader(RuntimeContext context) { EnumSet<Parser.Option> nativeOptions = EnumSet.of(Parser.Option.NativeCall, Parser.Option.NativeFunction); nativeOptions.addAll(context.getParserOptions()); /* @formatter:off */ RuntimeContext nativeContext = new RuntimeContext.Builder(context) .setParserOptions(nativeOptions) .build(); /* @formatter:on */ return new ScriptLoader(nativeContext); } }