Skip to content

Commit

Permalink
Merge pull request #493 from billdawson/timob-5123
Browse files Browse the repository at this point in the history
TIMOB-5123 Use Rhino native require() CommonJS implementation
  • Loading branch information
marshall committed Sep 28, 2011
2 parents 6c2ee0c + 076cb69 commit 19190ee
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 65 deletions.
Binary file modified android/titanium/lib/smalljs.jar
Binary file not shown.
23 changes: 23 additions & 0 deletions android/titanium/src/org/appcelerator/titanium/TiScriptRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.appcelerator.titanium.util.Log;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeFunction;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
Expand Down Expand Up @@ -86,6 +87,28 @@ public void setAppPackageName(String packageName)
appPackageName = packageName;
}

public Script getScript(Context context, Scriptable scope, String relativePath)
throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
String scriptClassName = getScriptClassName(relativePath);
TiScript tiScript = scripts.get(scriptClassName);
if (tiScript != null) {
return tiScript.script;
}
Class<?> scriptClass = Class.forName(scriptClassName);
tiScript = new TiScript();
tiScript.context = context;
tiScript.scope = scope;
if (scriptClass != null) {
Object scriptObj = scriptClass.newInstance();
tiScript.script = (Script) scriptObj;
tiScript.name = scriptClass.getName();
scripts.put(tiScript.name, tiScript);
return tiScript.script;
} else {
throw new ClassNotFoundException("CommonJS module class for \"" + relativePath + "\" not found.");
}
}

public Object runScript(Context context, Scriptable scope, String relativePath)
throws ClassNotFoundException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.commonjs.module.ModuleScript;
import org.mozilla.javascript.commonjs.module.ModuleScriptProvider;
import org.mozilla.javascript.commonjs.module.Require;
import org.mozilla.javascript.commonjs.module.RequireBuilder;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.os.Process;

public class KrollContext implements Handler.Callback
public class KrollContext implements Handler.Callback, ModuleScriptProvider
{
private static final String LCAT = "KrollContext";
private static boolean DBG = TiConfig.DEBUG;
Expand All @@ -54,6 +59,7 @@ public class KrollContext implements Handler.Callback
private static KrollThreadListener threadListener;

private KrollHandlerThread thread;
private Require commonJsRequire;
private TiContext tiContext;
private ScriptableObject jsScope;
private String sourceUrl;
Expand Down Expand Up @@ -189,12 +195,78 @@ protected void initContext()
if (DBG) {
Log.d(LCAT, "Initialized scope: " + jsScope);
}
this.commonJsRequire = buildCommonJsRequire(ctx);
if (DBG) {
Log.d(LCAT, "Initialized commonJS require() function: " + this.commonJsRequire);
}
initialized.countDown();
} finally {
exit();
}
}

private Require buildCommonJsRequire(Context ctx)
{
RequireBuilder builder = new RequireBuilder();
builder.setModuleScriptProvider(this);
return builder.createRequire(ctx, jsScope);
}

@Override
public ModuleScript getModuleScript(Context context, String moduleId,
Scriptable paths) throws Exception
{
Script script = null;
String uri;

StringBuilder sb = new StringBuilder();
sb.append(TiC.URL_APP_PREFIX)
.append(moduleId)
.append(".js");
uri = sb.toString();

if (useOptimization) {
// get Script from compiled script class
script = TiScriptRunner.getInstance()
.getScript(context, jsScope, moduleId);
if (script == null) {
Log.e(LCAT, "Could not retrieve a Script object for module '" + moduleId + "'.");
Context.throwAsScriptRuntimeEx(new Exception("Unable to load Script for module '" + moduleId + "'."));
}
} else {
// make Script from JS source
TiBaseFile file = TiFileFactory.createTitaniumFile(tiContext, new String[] { uri }, false);
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(file.getInputStream()), 4000);
script = context.compileReader(br, uri, 1, null);
} catch (IOException e) {
Log.e(LCAT, "IOException reading module file: " + uri, e);
Context.throwAsScriptRuntimeEx(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// Ignore
}
}
}
}

return new ModuleScript(script, uri);
}

public Object callCommonJsRequire(String path)
{
Context ctx = enter();
try {
return commonJsRequire.call(ctx, jsScope, jsScope, new String[] { path });
} finally {
exit();
}
}

protected void threadEnded()
{
if (threadListener != null) {
Expand Down
78 changes: 14 additions & 64 deletions android/titanium/src/ti/modules/titanium/TitaniumModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiBaseActivity;
import org.appcelerator.titanium.TiBlob;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.TiContext;
import org.appcelerator.titanium.TiLaunchActivity;
import org.appcelerator.titanium.io.TiBaseFile;
import org.appcelerator.titanium.io.TiFileFactory;
import org.appcelerator.titanium.kroll.KrollCallback;
import org.appcelerator.titanium.kroll.KrollContext;
import org.appcelerator.titanium.kroll.KrollCoverage;
Expand Down Expand Up @@ -424,7 +421,7 @@ protected KrollModule requireNativeModule(TiContext context, String path)
}

@Kroll.method @Kroll.topLevel
public KrollProxy require(KrollInvocation invocation, String path)
public Object require(KrollInvocation invocation, String path)
{
// 1. look for a native module first
// 2. then look for a cached module
Expand All @@ -443,71 +440,24 @@ public KrollProxy require(KrollInvocation invocation, String path)
return module;
}

// NOTE: CommonJS modules load absolute to app:// in Titanium
builder.setLength(0);
builder.append(TiC.URL_APP_PREFIX)
.append(path)
.append(".js");
String fileUrl = builder.toString();
TiBaseFile tbf = TiFileFactory.createTitaniumFile(ctx, new String[]{ fileUrl }, false);
if (tbf == null) {
//the spec says we are required to throw an exception
Context.reportError("Couldn't find module: " + path);
return null;
}

if (DBG) {
Log.d(LCAT, "Attempting to include JS module: " + tbf.nativePath());
Log.d(LCAT, "Attempting to include CommonJS module: " + path);
}
try {
TiBlob blob = (TiBlob) tbf.read();
if (blob == null) {
Log.e(LCAT, "Couldn't read required file: " + fileUrl);
return null;
}

// TODO: we need to switch to the Rhino native require()
// implementation, but in the meantime this will have to do

// create the CommonJS exporter
KrollProxy proxy = new KrollProxy(ctx);
try {
return ctx.getKrollContext().callCommonJsRequire(path);
} catch (Exception e) {
builder.setLength(0);
builder.append("(function(exports){")
.append(blob.getText())
.append("return exports;")
.append("})({})");

Object result = ctx.evalJS(builder.toString());

if (!(result instanceof Scriptable)) {
builder.setLength(0);
builder.append("Module did not correctly return an exports object: ")
.append(path)
.append(", result: ")
.append(result);
Context.throwAsScriptRuntimeEx(new Exception(builder.toString()));
return null;
}

Scriptable exports = (Scriptable) result;
// CommonJS modules export all functions/properties as
// properties of the special exports object provided
for (Object key : exports.getIds()) {
String propName = key.toString();
proxy.setProperty(propName, exports.get(propName, exports));
}

// spec says you must have a read-only id property - we don't
// currently support readonly in kroll so this is probably OK for now
proxy.setProperty(TiC.PROPERTY_ID, path);
// uri is optional but we point it to where we loaded it
proxy.setProperty(TiC.PROPERTY_URI, fileUrl);
return proxy;
} catch (Exception ex) {
Log.e(LCAT, "Error loading module named: " + path, ex);
Context.throwAsScriptRuntimeEx(ex);
return null;
builder.append("require(\"")
.append(path)
.append("\") failed: ")
.append(e.getMessage());
String msg = builder.toString();
Log.e(LCAT, msg, e);
Context.throwAsScriptRuntimeEx(new Exception(msg));
}

return Context.throwAsScriptRuntimeEx(new Exception("Cannot find module '" + path + "'"));
}

@Kroll.method
Expand Down

0 comments on commit 19190ee

Please sign in to comment.