mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Implementing uneval/toSource functionality of SpiderMonkey. Foe details, see http://bugzilla.mozilla.org/show_bug.cgi?id=225465.
This commit is contained in:
parent
56ad1b1cba
commit
2e75448c13
@ -91,6 +91,21 @@ public class BaseFunction extends IdScriptable implements Function {
|
||||
functionName);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
int indent = 0;
|
||||
int flags = Decompiler.TO_SOURCE_FLAG;
|
||||
if (args.length != 0) {
|
||||
indent = ScriptRuntime.toInt32(args[0]);
|
||||
if (indent >= 0) {
|
||||
flags = 0;
|
||||
} else {
|
||||
indent = 0;
|
||||
}
|
||||
}
|
||||
return decompile(cx, indent, flags);
|
||||
}
|
||||
|
||||
protected int getIdAttributes(int id)
|
||||
{
|
||||
switch (id) {
|
||||
|
@ -78,6 +78,11 @@ public class Decompiler
|
||||
*/
|
||||
public static final int ONLY_BODY_FLAG = 1 << 0;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the decompilation generates toSource result.
|
||||
*/
|
||||
public static final int TO_SOURCE_FLAG = 1 << 1;
|
||||
|
||||
/**
|
||||
* Decompilation property to specify initial ident value.
|
||||
*/
|
||||
@ -304,6 +309,7 @@ public class Decompiler
|
||||
|
||||
StringBuffer result = new StringBuffer();
|
||||
boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
|
||||
boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG));
|
||||
|
||||
// Spew tokens in source, for debugging.
|
||||
// as TYPE number char
|
||||
@ -332,11 +338,6 @@ public class Decompiler
|
||||
System.err.println();
|
||||
}
|
||||
|
||||
// add an initial newline to exactly match js.
|
||||
result.append('\n');
|
||||
for (int j = 0; j < indent; j++)
|
||||
result.append(' ');
|
||||
|
||||
int braceNesting = 0;
|
||||
boolean afterFirstEOL = false;
|
||||
int i = 0;
|
||||
@ -348,6 +349,17 @@ public class Decompiler
|
||||
topFunctionType = source.charAt(i + 1);
|
||||
}
|
||||
|
||||
if (!toSource) {
|
||||
// add an initial newline to exactly match js.
|
||||
result.append('\n');
|
||||
for (int j = 0; j < indent; j++)
|
||||
result.append(' ');
|
||||
} else {
|
||||
if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
|
||||
result.append('(');
|
||||
}
|
||||
}
|
||||
|
||||
while (i < length) {
|
||||
switch(source.charAt(i)) {
|
||||
case Token.NAME:
|
||||
@ -380,7 +392,7 @@ public class Decompiler
|
||||
break;
|
||||
|
||||
case Token.FUNCTION:
|
||||
++i; // skip function type, it is used only when decompiling
|
||||
++i; // skip function type
|
||||
result.append("function ");
|
||||
break;
|
||||
|
||||
@ -441,6 +453,7 @@ public class Decompiler
|
||||
break;
|
||||
|
||||
case Token.EOL: {
|
||||
if (toSource) break;
|
||||
boolean newLine = true;
|
||||
if (!afterFirstEOL) {
|
||||
afterFirstEOL = true;
|
||||
@ -785,9 +798,15 @@ public class Decompiler
|
||||
++i;
|
||||
}
|
||||
|
||||
// add that trailing newline if it's an outermost function.
|
||||
if (!justFunctionBody)
|
||||
result.append('\n');
|
||||
if (!toSource) {
|
||||
// add that trailing newline if it's an outermost function.
|
||||
if (!justFunctionBody)
|
||||
result.append('\n');
|
||||
} else {
|
||||
if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
|
||||
result.append(')');
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
@ -158,12 +158,12 @@ public class NativeArray extends IdScriptable {
|
||||
return jsConstructor(cx, scope, args, f, thisObj == null);
|
||||
|
||||
case Id_toString:
|
||||
return toStringHelper(cx, thisObj,
|
||||
return toStringHelper(cx, scope, thisObj,
|
||||
cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE),
|
||||
false);
|
||||
|
||||
case Id_toLocaleString:
|
||||
return toStringHelper(cx, thisObj, false, true);
|
||||
return toStringHelper(cx, scope, thisObj, false, true);
|
||||
|
||||
case Id_join:
|
||||
return js_join(cx, thisObj, args);
|
||||
@ -317,6 +317,13 @@ public class NativeArray extends IdScriptable {
|
||||
return super.getDefaultValue(hint);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return toStringHelper(cx, scope, this, true, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See ECMA 15.4.1,2
|
||||
*/
|
||||
@ -449,7 +456,8 @@ public class NativeArray extends IdScriptable {
|
||||
}
|
||||
}
|
||||
|
||||
private static String toStringHelper(Context cx, Scriptable thisObj,
|
||||
private static String toStringHelper(Context cx, Scriptable scope,
|
||||
Scriptable thisObj,
|
||||
boolean toSource, boolean toLocale)
|
||||
throws JavaScriptException
|
||||
{
|
||||
@ -498,7 +506,10 @@ public class NativeArray extends IdScriptable {
|
||||
}
|
||||
haslast = true;
|
||||
|
||||
if (elem instanceof String) {
|
||||
if (toSource) {
|
||||
result.append(ScriptRuntime.uneval(cx, scope, elem));
|
||||
|
||||
} else if (elem instanceof String) {
|
||||
String s = (String)elem;
|
||||
if (toSource) {
|
||||
result.append('\"');
|
||||
@ -507,6 +518,7 @@ public class NativeArray extends IdScriptable {
|
||||
} else {
|
||||
result.append(s);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (toLocale && elem != Undefined.instance &&
|
||||
elem != null)
|
||||
|
@ -66,6 +66,14 @@ final class NativeBoolean extends IdScriptable {
|
||||
return super.getDefaultValue(typeHint);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
if (booleanValue)
|
||||
return "(new Boolean(true))";
|
||||
else
|
||||
return "(new Boolean(false))";
|
||||
}
|
||||
|
||||
public int methodArity(int methodId) {
|
||||
if (prototypeFlag) {
|
||||
if (methodId == Id_constructor) return 1;
|
||||
|
@ -91,6 +91,11 @@ final class NativeDate extends IdScriptable {
|
||||
super.fillConstructorProperties(cx, ctor, sealed);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
return "(new Date("+ScriptRuntime.toString(date)+"))";
|
||||
}
|
||||
|
||||
public int methodArity(int methodId) {
|
||||
if (prototypeFlag) {
|
||||
switch (methodId) {
|
||||
|
@ -55,6 +55,17 @@ final class NativeError extends IdScriptable
|
||||
obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Object value = messageValue;
|
||||
if (value == NOT_FOUND)
|
||||
return "(new Error())";
|
||||
String str = ScriptRuntime.toString(value);
|
||||
str = ScriptRuntime.escapeString(str);
|
||||
return "(new Error(\""+str+"\"))";
|
||||
}
|
||||
|
||||
protected int getIdAttributes(int id)
|
||||
{
|
||||
if (id == Id_message || id == Id_name) { return EMPTY; }
|
||||
|
@ -70,6 +70,7 @@ public class NativeGlobal implements Serializable, IdFunctionMaster
|
||||
case Id_parseFloat: name = "parseFloat"; break;
|
||||
case Id_parseInt: name = "parseInt"; break;
|
||||
case Id_unescape: name = "unescape"; break;
|
||||
case Id_uneval: name = "uneval"; break;
|
||||
default:
|
||||
Kit.codeBug(); name = null;
|
||||
}
|
||||
@ -173,6 +174,12 @@ public class NativeGlobal implements Serializable, IdFunctionMaster
|
||||
case Id_unescape:
|
||||
return js_unescape(cx, args);
|
||||
|
||||
case Id_uneval: {
|
||||
Object value = (args.length != 0)
|
||||
? args[0] : Undefined.instance;
|
||||
return ScriptRuntime.uneval(cx, scope, value);
|
||||
}
|
||||
|
||||
case Id_new_CommonError:
|
||||
return new_CommonError(function, cx, scope, args);
|
||||
}
|
||||
@ -194,6 +201,7 @@ public class NativeGlobal implements Serializable, IdFunctionMaster
|
||||
case Id_parseFloat: return 1;
|
||||
case Id_parseInt: return 2;
|
||||
case Id_unescape: return 1;
|
||||
case Id_uneval: return 1;
|
||||
|
||||
case Id_new_CommonError: return 1;
|
||||
}
|
||||
@ -748,10 +756,11 @@ public class NativeGlobal implements Serializable, IdFunctionMaster
|
||||
Id_parseFloat = 9,
|
||||
Id_parseInt = 10,
|
||||
Id_unescape = 11,
|
||||
Id_uneval = 12,
|
||||
|
||||
LAST_SCOPE_FUNCTION_ID = 11,
|
||||
LAST_SCOPE_FUNCTION_ID = 12,
|
||||
|
||||
Id_new_CommonError = 12;
|
||||
Id_new_CommonError = 13;
|
||||
|
||||
private boolean scopeSlaveFlag;
|
||||
|
||||
|
@ -56,6 +56,11 @@ final class NativeMath extends IdScriptable
|
||||
|
||||
public String getClassName() { return "Math"; }
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
return "Math";
|
||||
}
|
||||
|
||||
protected int getIdAttributes(int id)
|
||||
{
|
||||
if (id > LAST_METHOD_ID) {
|
||||
|
@ -86,6 +86,11 @@ final class NativeNumber extends IdScriptable {
|
||||
super.fillConstructorProperties(cx, ctor, sealed);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
return "(new Number("+ScriptRuntime.toString(doubleValue)+"))";
|
||||
}
|
||||
|
||||
public int methodArity(int methodId) {
|
||||
if (prototypeFlag) {
|
||||
switch (methodId) {
|
||||
|
@ -62,6 +62,7 @@ public class NativeObject extends IdScriptable
|
||||
protected int mapNameToId(String s) { return 0; }
|
||||
|
||||
protected String getIdName(int id) { return null; }
|
||||
|
||||
}
|
||||
|
||||
final class NativeObjectPrototype extends NativeObject
|
||||
@ -81,6 +82,7 @@ final class NativeObjectPrototype extends NativeObject
|
||||
case Id_hasOwnProperty: return 1;
|
||||
case Id_propertyIsEnumerable: return 1;
|
||||
case Id_isPrototypeOf: return 1;
|
||||
case Id_toSource: return 0;
|
||||
}
|
||||
return super.methodArity(methodId);
|
||||
}
|
||||
@ -104,9 +106,20 @@ final class NativeObjectPrototype extends NativeObject
|
||||
return ScriptRuntime.toObject(cx, scope, args[0]);
|
||||
}
|
||||
|
||||
case Id_toString:
|
||||
case Id_toLocaleString: /* For now just alias toString */
|
||||
return js_toString(cx, thisObj);
|
||||
case Id_toLocaleString: // For now just alias toString
|
||||
case Id_toString: {
|
||||
if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) {
|
||||
String s = toSource(cx, scope, thisObj, args);
|
||||
int L = s.length();
|
||||
if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')')
|
||||
{
|
||||
// Strip () that surrounds toSource
|
||||
s = s.substring(1, L - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return toString(thisObj);
|
||||
}
|
||||
|
||||
case Id_valueOf:
|
||||
return thisObj;
|
||||
@ -147,75 +160,27 @@ final class NativeObjectPrototype extends NativeObject
|
||||
}
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
case Id_toSource:
|
||||
return toSource(cx, scope, thisObj, args);
|
||||
}
|
||||
return super.execMethod(methodId, f, cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
static String toString(Scriptable thisObj)
|
||||
{
|
||||
Context cx = Context.getCurrentContext();
|
||||
if (cx != null) {
|
||||
return js_toString(cx, thisObj);
|
||||
} else {
|
||||
return "[object " + thisObj.getClassName() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private static String js_toString(Context cx, Scriptable thisObj)
|
||||
{
|
||||
if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) {
|
||||
return toSource(cx, thisObj);
|
||||
}
|
||||
return "[object " + thisObj.getClassName() + "]";
|
||||
}
|
||||
|
||||
private static String toSource(Context cx, Scriptable thisObj)
|
||||
private static String toSource(Context cx, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
StringBuffer result = new StringBuffer(256);
|
||||
result.append('{');
|
||||
|
||||
boolean toplevel, iterating;
|
||||
if (cx.iterating == null) {
|
||||
toplevel = true;
|
||||
iterating = false;
|
||||
cx.iterating = new ObjToIntMap(31);
|
||||
} else {
|
||||
toplevel = false;
|
||||
iterating = cx.iterating.has(thisObj);
|
||||
if (thisObj instanceof ScriptableObject) {
|
||||
ScriptableObject so = (ScriptableObject)thisObj;
|
||||
return so.toSource(cx, scope, args);
|
||||
}
|
||||
|
||||
// Make sure cx.iterating is set to null when done
|
||||
// so we don't leak memory
|
||||
try {
|
||||
if (!iterating) {
|
||||
cx.iterating.put(thisObj, 0); // stop recursion.
|
||||
Object[] ids = thisObj.getIds();
|
||||
for(int i=0; i < ids.length; i++) {
|
||||
if (i > 0)
|
||||
result.append(", ");
|
||||
Object id = ids[i];
|
||||
result.append(id);
|
||||
result.append(':');
|
||||
Object p = (id instanceof String)
|
||||
? thisObj.get((String) id, thisObj)
|
||||
: thisObj.get(((Integer) id).intValue(), thisObj);
|
||||
if (p instanceof String) {
|
||||
result.append('\"');
|
||||
result.append(ScriptRuntime.escapeString((String)p));
|
||||
result.append('\"');
|
||||
} else {
|
||||
result.append(ScriptRuntime.toString(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (toplevel) {
|
||||
cx.iterating = null;
|
||||
}
|
||||
}
|
||||
|
||||
result.append('}');
|
||||
return result.toString();
|
||||
return ScriptRuntime.toString(thisObj);
|
||||
}
|
||||
|
||||
protected String getIdName(int id)
|
||||
@ -228,6 +193,7 @@ final class NativeObjectPrototype extends NativeObject
|
||||
case Id_hasOwnProperty: return "hasOwnProperty";
|
||||
case Id_propertyIsEnumerable: return "propertyIsEnumerable";
|
||||
case Id_isPrototypeOf: return "isPrototypeOf";
|
||||
case Id_toSource: return "toSource";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -237,11 +203,14 @@ final class NativeObjectPrototype extends NativeObject
|
||||
protected int mapNameToId(String s)
|
||||
{
|
||||
int id;
|
||||
// #generated# Last update: 2001-04-24 12:37:03 GMT+02:00
|
||||
// #generated# Last update: 2003-11-11 01:51:40 CET
|
||||
L0: { id = 0; String X = null; int c;
|
||||
L: switch (s.length()) {
|
||||
case 7: X="valueOf";id=Id_valueOf; break L;
|
||||
case 8: X="toString";id=Id_toString; break L;
|
||||
case 8: c=s.charAt(3);
|
||||
if (c=='o') { X="toSource";id=Id_toSource; }
|
||||
else if (c=='t') { X="toString";id=Id_toString; }
|
||||
break L;
|
||||
case 11: X="constructor";id=Id_constructor; break L;
|
||||
case 13: X="isPrototypeOf";id=Id_isPrototypeOf; break L;
|
||||
case 14: c=s.charAt(0);
|
||||
@ -264,7 +233,8 @@ final class NativeObjectPrototype extends NativeObject
|
||||
Id_hasOwnProperty = 5,
|
||||
Id_propertyIsEnumerable = 6,
|
||||
Id_isPrototypeOf = 7,
|
||||
MAX_PROTOTYPE_ID = 7;
|
||||
Id_toSource = 8,
|
||||
MAX_PROTOTYPE_ID = 8;
|
||||
|
||||
// #/string_id_map#
|
||||
}
|
||||
|
@ -72,6 +72,11 @@ final class NativeString extends IdScriptable {
|
||||
super.fillConstructorProperties(cx, ctor, sealed);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
{
|
||||
return "(new String(\""+ScriptRuntime.escapeString(string)+"\"))";
|
||||
}
|
||||
|
||||
protected int getIdAttributes(int id)
|
||||
{
|
||||
if (id == Id_length) {
|
||||
|
@ -548,6 +548,49 @@ public class ScriptRuntime {
|
||||
|
||||
}
|
||||
|
||||
static String uneval(Context cx, Scriptable scope, Object value)
|
||||
throws JavaScriptException
|
||||
{
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
if (value instanceof String) {
|
||||
String escaped = escapeString((String)value);
|
||||
StringBuffer sb = new StringBuffer(escaped.length() + 2);
|
||||
sb.append('\"');
|
||||
sb.append(escaped);
|
||||
sb.append('\"');
|
||||
return sb.toString();
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
double d = ((Number)value).doubleValue();
|
||||
if (d == 0 && 1 / d < 0) {
|
||||
return "-0";
|
||||
}
|
||||
return toString(d);
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return toString(value);
|
||||
}
|
||||
if (value == Undefined.instance) {
|
||||
return "undefined";
|
||||
}
|
||||
if (value instanceof Scriptable) {
|
||||
Scriptable obj = (Scriptable)value;
|
||||
Object v = ScriptableObject.getProperty(obj, "toSource");
|
||||
if (v instanceof Function) {
|
||||
Function f = (Function)v;
|
||||
return toString(f.call(cx, scope, obj, emptyArgs));
|
||||
}
|
||||
if (value instanceof ScriptableObject) {
|
||||
ScriptableObject so = (ScriptableObject)obj;
|
||||
return so.toSource(cx, scope, emptyArgs);
|
||||
}
|
||||
return toString(value);
|
||||
}
|
||||
throw errorWithClassName("msg.invalid.type", value);
|
||||
}
|
||||
|
||||
public static Scriptable toObject(Scriptable scope, Object val)
|
||||
{
|
||||
if (val instanceof Scriptable && val != Undefined.instance) {
|
||||
|
@ -677,6 +677,50 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
||||
return ScriptRuntime.jsDelegatesTo(instance, this);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
StringBuffer result = new StringBuffer(256);
|
||||
result.append("({");
|
||||
|
||||
boolean toplevel, iterating;
|
||||
if (cx.iterating == null) {
|
||||
toplevel = true;
|
||||
iterating = false;
|
||||
cx.iterating = new ObjToIntMap(31);
|
||||
} else {
|
||||
toplevel = false;
|
||||
iterating = cx.iterating.has(this);
|
||||
}
|
||||
|
||||
// Make sure cx.iterating is set to null when done
|
||||
// so we don't leak memory
|
||||
try {
|
||||
if (!iterating) {
|
||||
cx.iterating.intern(this); // stop recursion.
|
||||
Object[] ids = this.getIds();
|
||||
for(int i=0; i < ids.length; i++) {
|
||||
if (i > 0)
|
||||
result.append(", ");
|
||||
Object id = ids[i];
|
||||
result.append(id);
|
||||
result.append(':');
|
||||
Object p = (id instanceof String)
|
||||
? this.get((String) id, this)
|
||||
: this.get(((Integer) id).intValue(), this);
|
||||
result.append(ScriptRuntime.uneval(cx, scope, p));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (toplevel) {
|
||||
cx.iterating = null;
|
||||
}
|
||||
}
|
||||
|
||||
result.append("})");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines JavaScript objects from a Java class that implements Scriptable.
|
||||
*
|
||||
|
@ -2625,8 +2625,15 @@ System.out.println("Testing at " + x.cp + ", op = " + op);
|
||||
throw ScriptRuntime.constructError("SyntaxError", msg);
|
||||
}
|
||||
|
||||
protected String toSource(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
|
||||
|
||||
protected int getIdAttributes(int id)
|
||||
{
|
||||
{
|
||||
switch (id) {
|
||||
case Id_lastIndex:
|
||||
return ScriptableObject.PERMANENT | ScriptableObject.DONTENUM;
|
||||
|
Loading…
Reference in New Issue
Block a user