/* * Copyright 2013 JBoss Inc * * 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 org.dynjs.runtime.builtins; import java.util.*; import org.dynjs.exception.ThrowException; import org.dynjs.runtime.*; import org.dynjs.runtime.modules.*; /** * Built-in implementation of <code>require(moduleName)</code>. * * @author Lance Ball * @author Bob McWhirter * http://wiki.commonjs.org/wiki/Modules/1.1 * @see ModuleProvider */ public class Require extends AbstractNativeFunction { private List<ModuleProvider> moduleProviders = new ArrayList<>(); private LinkedList<String> loadPaths = new LinkedList<>(); public Require(GlobalContext globalContext) { super(globalContext, "name"); DynObject module = new DynObject(globalContext); module.put("id", "dynjs"); module.put("exports", new DynObject(globalContext)); globalContext.getObject().put( null, "module", module, false); globalContext.getObject().put( null, "exports", module.get("exports"), false ); // ---------------------------------------- // Module-provider setup // ---------------------------------------- JavaClassModuleProvider javaClassModuleProvider = new JavaClassModuleProvider(); javaClassModuleProvider.addModule(new ConsoleModule()); javaClassModuleProvider.addModule(new UtilModule()); this.moduleProviders.add(javaClassModuleProvider); this.moduleProviders.add(new ClasspathModuleProvider()); this.moduleProviders.add(new FilesystemModuleProvider(this)); String customRequirePath = this.getCustomRequirePath(); if (customRequirePath != null) { String[] paths = customRequirePath.split(":"); Collections.addAll(this.loadPaths, paths); } this.loadPaths.add(System.getProperty("user.dir") + "/"); this.put("paths", loadPaths); this.put("addLoadPath", new AbstractNativeFunction(globalContext) { @Override public Object call(ExecutionContext context, Object self, Object... args) { if (args[0] == null || args[0] == Types.UNDEFINED || args[0] == Types.NULL) { return loadPaths; } addLoadPath((String) args[0]); return loadPaths; } }); this.put("removeLoadPath", new AbstractNativeFunction(globalContext) { @Override public Object call(ExecutionContext context, Object self, Object... args) { if (args[0] == null || args[0] == Types.UNDEFINED || args[0] == Types.NULL) { return Types.NULL; } removeLoadPath((String) args[0]); return loadPaths; } }); this.put("pushLoadPath", new AbstractNativeFunction(globalContext) { @Override public Object call(ExecutionContext context, Object self, Object... args) { if (args[0] == null || args[0] == Types.UNDEFINED || args[0] == Types.NULL) { return Types.NULL; } pushLoadPath((String) args[0]); return loadPaths; } }); } public List<ModuleProvider> getModuleProviders() { return this.moduleProviders; } public void addModuleProvider(ModuleProvider provider) { this.moduleProviders.add(provider); } public void removeLoadPath(String path) { loadPaths.remove(path); this.put("paths", loadPaths); } public void addLoadPath(String path) { this.loadPaths.add(path); this.put("paths", loadPaths); } public void pushLoadPath(String path) { this.loadPaths.push(path); this.put("paths", loadPaths); } @Override public Object call(ExecutionContext context, Object self, Object... arguments) { if (arguments[0] == Types.UNDEFINED) { throw new ThrowException(context, context.createError("Error", "no module identifier provided")); } String moduleName = (String) arguments[0]; List<ModuleProvider> moduleProviders = this.getModuleProviders(); // Load module providers in reverse order for (int i = moduleProviders.size(); i > 0; i--) { ModuleProvider provider = moduleProviders.get(i-1); // if a module provider can generate a module ID, then it can load the module. String moduleId = provider.generateModuleID(context, moduleName); if (moduleId != null) { // first check to see if we have the module cached, if so return it if (cache.containsKey(moduleId)) { return cache.get(moduleId); } // add ID + empty module.exports object to cache GlobalContext globalContext = context.getGlobalContext(); JSObject module = new DynObject(globalContext); JSObject exports = new DynObject(globalContext); module.put(context, "exports", exports, true); module.put(context, "id", moduleId, false); cache.put(moduleId, exports); // setup our module/exports/id in the execution context ModuleProvider.setLocalVar(context, "module", module); ModuleProvider.setLocalVar(context, "exports", exports); ModuleProvider.setLocalVar(context, "id", moduleId); if (provider.load(context, moduleId)) { final Object exported = module.get(context, "exports"); cache.put(moduleId, exported); return exported; }; return null; } } throw new ThrowException(context, context.createError("Error", "cannot load module " + moduleName)); } private String getCustomRequirePath() { if (System.getenv("DYNJS_REQUIRE_PATH") != null) { return System.getenv("DYNJS_REQUIRE_PATH"); } return System.getProperty("dynjs.require.path"); } @Override public void setFileName() { this.filename = "org/dynjs/runtime/builtins/Require.java"; } @Override public void setupDebugContext() { this.debugContext = "<native function: require>"; } public List<String> getLoadPaths() { return loadPaths; } private final HashMap<String, Object> cache = new HashMap<>(); }