-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TIMOB-5123 Use Rhino native require() CommonJS implementation #493
Changes from 5 commits
1cccac7
85e0dd4
a508517
a92bc7a
ed5436e
adfccf3
4388db8
c5c9db2
5cf22ff
29ea073
8746e2c
076cb69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,8 +28,13 @@ | |
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; | ||
|
@@ -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; | ||
|
@@ -189,12 +195,73 @@ 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(new ModuleScriptProvider() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is ModuleScriptProvider an interface? it may be better to implement directly on KrollContext to save ourselves yet another class to dex :) |
||
{ | ||
@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); | ||
br.close(); | ||
} catch (IOException e) { | ||
Log.e(LCAT, "IOException reading module file: " + uri, e); | ||
Context.throwAsScriptRuntimeEx(e); | ||
} | ||
} | ||
|
||
return new ModuleScript(script, uri); | ||
} | ||
}); | ||
|
||
return builder.createRequire(ctx, jsScope); | ||
} | ||
|
||
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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -443,71 +440,59 @@ public KrollProxy require(KrollInvocation invocation, String path) | |
return module; | ||
} | ||
|
||
// NOTE: CommonJS modules load absolute to app:// in Titanium | ||
if (DBG) { | ||
Log.d(LCAT, "Attempting to include CommonJS module: " + path); | ||
} | ||
|
||
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()); | ||
} | ||
try { | ||
TiBlob blob = (TiBlob) tbf.read(); | ||
if (blob == null) { | ||
Log.e(LCAT, "Couldn't read required file: " + fileUrl); | ||
return null; | ||
} | ||
// create the CommonJS exporter | ||
KrollProxy proxy = new KrollProxy(ctx); | ||
|
||
// 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); | ||
// call the actual require() implementation. | ||
Object result = null; | ||
try { | ||
result = 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)); | ||
} | ||
builder.append("require(\"") | ||
.append(path) | ||
.append("\") failed: ") | ||
.append(e.getMessage()); | ||
String msg = builder.toString(); | ||
Log.e(LCAT, msg, e); | ||
Context.throwAsScriptRuntimeEx(new Exception(msg)); | ||
} | ||
|
||
// 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); | ||
if (!(result instanceof Scriptable)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't "exports" actually be any old type? (not just an object?) i.e: var x = require("x");
// x.js
module.exports = 1; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good puzzle to work on. :) I'd never seen code like that ( Our call to Rhino's So I guess Jeff put this check in because he wanted to be sure to be able to use Nevertheless, this check seems unnecessary as you say. And I'm not sure it's necessary that we absolutely return a |
||
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; | ||
|
||
} | ||
|
||
@Kroll.method | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the NativeFunction specialization (and
@SuppressWarnings
) necessary here? you're casting directly to Script on the line below