-----
The patch fixes a bug in getIds method where the assignment "result =
tmp" was missed, adds the public method activateIdMap(int maxId) to
IdScriptable and changes setAttributes method not to allow setting of
attributes that are less restrictive then ones returned by
getIdDefaultAttributes. That was supposed to be the case and the patch
makes it explicit.
-----
The patch makes BaseFunction.setImmunePrototypeProperty public so it can
be called from other packages (regexp).
-----
The patch switches NativeRegExp and NativeRegExpCtor to use
IdScriptable. It also changes code in a few places to passes Context and
RegExpImpl directly instead of using Context.getCurrentContext().

The patch also fixes a bug when

for (var i in RegExp) { print(i); }

would not include $1..$9 in the output in violation with Ecma. It was
caused by not overriding ScriptableObject.getIds in
NativeRegExpCtor.
This commit is contained in:
nboyd%atg.com 2001-05-29 14:07:49 +00:00
Родитель ab5da9f316
Коммит 8b34980fb3
6 изменённых файлов: 501 добавлений и 323 удалений

Просмотреть файл

@ -51,6 +51,7 @@ public class BaseFunction extends IdScriptable implements Function {
BaseFunction obj = new BaseFunction();
obj.prototypeFlag = true;
obj.functionName = "";
obj.prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;
obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);
}
@ -188,7 +189,7 @@ public class BaseFunction extends IdScriptable implements Function {
* Make value as DontEnum, DontDelete, ReadOnly
* prototype property of this Function object
*/
protected void setImmunePrototypeProperty(Object value) {
public void setImmunePrototypeProperty(Object value) {
prototypeProperty = (value != null) ? value : NULL_TAG;
prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;
}
@ -246,7 +247,7 @@ public class BaseFunction extends IdScriptable implements Function {
sb.append(getFunctionName());
sb.append("() {\n\t");
}
sb.append("[native code, arity=]\n");
sb.append("[native code, arity=");
sb.append(getArity());
sb.append("]\n");
if (!justbody) {
@ -353,11 +354,13 @@ public class BaseFunction extends IdScriptable implements Function {
// mode.
int oldOptLevel = cx.getOptimizationLevel();
cx.setOptimizationLevel(-1);
NativeFunction fn = (NativeFunction) cx.compileFunction(
global, source,
filename, linep[0],
securityDomain);
cx.setOptimizationLevel(oldOptLevel);
NativeFunction fn;
try {
fn = (NativeFunction) cx.compileFunction(global, source,
filename, linep[0],
securityDomain);
}
finally { cx.setOptimizationLevel(oldOptLevel); }
fn.functionName = "anonymous";
fn.setPrototype(getFunctionPrototype(global));
@ -512,7 +515,7 @@ public class BaseFunction extends IdScriptable implements Function {
protected String functionName;
private Object prototypeProperty;
private int prototypePropertyAttrs = EMPTY;
private int prototypePropertyAttrs = DONTENUM;
private boolean prototypeFlag;
}

Просмотреть файл

@ -64,7 +64,7 @@ public abstract class IdScriptable extends ScriptableObject
protected static final Object NULL_TAG = new Object();
public IdScriptable() {
maxId = maxInstanceId();
activateIdMap(maxInstanceId());
}
public boolean has(String name, Scriptable start) {
@ -172,17 +172,6 @@ public abstract class IdScriptable extends ScriptableObject
synchronized void addPropertyAttribute(int attribute) {
extraIdAttributes |= attribute;
if (maxId != 0) {
byte[] array = attributesArray;
if (array != null) {
for (int i = attributesArray.length; i-- != 0;) {
int old = array[i];
if (old != 0) {
array[i] = (byte)(attribute | old);
}
}
}
}
super.addPropertyAttribute(attribute);
}
@ -212,6 +201,7 @@ public abstract class IdScriptable extends ScriptableObject
Object[] tmp = new Object[result.length + count];
System.arraycopy(result, 0, tmp, 0, result.length);
System.arraycopy(ids, 0, tmp, result.length, count);
result = tmp;
}
}
}
@ -309,6 +299,11 @@ public abstract class IdScriptable extends ScriptableObject
return -1;
}
/** Activate id support with the given maximum id */
protected void activateIdMap(int maxId) {
this.maxId = maxId;
}
/** Sets whether newly constructed function objects should be sealed */
protected void setSealFunctionsFlag(boolean sealed) {
if (sealed) { setupFlags |= SEAL_FUNCTIONS_FLAG; }
@ -341,7 +336,7 @@ public abstract class IdScriptable extends ScriptableObject
public void addAsPrototype(int maxId, Context cx, Scriptable scope,
boolean sealed)
{
this.maxId = maxId;
activateIdMap(maxId);
setSealFunctionsFlag(sealed);
setFunctionParametrs(cx);
@ -469,6 +464,7 @@ public abstract class IdScriptable extends ScriptableObject
}
}
// Must be called only from synchronized (this)
private Object[] ensureIdData() {
Object[] data = idMapData;
if (data == null) {
@ -478,17 +474,25 @@ public abstract class IdScriptable extends ScriptableObject
}
private int getAttributes(int id) {
int attributes;
int attributes = getIdDefaultAttributes(id) | extraIdAttributes;
byte[] array = attributesArray;
if (array == null || (attributes = array[id - 1]) == 0) {
attributes = getIdDefaultAttributes(id) | extraIdAttributes;
if (array != null) {
attributes |= 0xFF & array[id - 1];
}
return VISIBLE_ATTR_MASK & attributes;
return attributes;
}
private void setAttributes(int id, int attributes) {
int defaultAttrs = getIdDefaultAttributes(id);
if ((attributes & defaultAttrs) != defaultAttrs) {
// It is a bug to set attributes to less restrictive values
// then given by defaultAttrs
throw new RuntimeException("Attempt to unset default attributes");
}
// Store only additional bits
attributes &= ~defaultAttrs;
byte[] array = attributesArray;
if (array == null) {
if (array == null && attributes != 0) {
synchronized (this) {
array = attributesArray;
if (array == null) {
@ -496,17 +500,13 @@ public abstract class IdScriptable extends ScriptableObject
}
}
}
attributes &= VISIBLE_ATTR_MASK;
array[id - 1] = (byte)(ASSIGNED_ATTRIBUTE_MASK | attributes);
if (array != null) {
array[id - 1] = (byte)attributes;
}
}
private static final boolean CACHE_NAMES = true;
private static final int
VISIBLE_ATTR_MASK = READONLY | DONTENUM | PERMANENT;
private static final int ASSIGNED_ATTRIBUTE_MASK = 0x80;
private int maxId;
private Object[] idMapData;
private byte[] attributesArray;

Просмотреть файл

@ -1231,19 +1231,15 @@ public class ScriptRuntime {
return ((NativeScript)jsThis).
exec(cx, ScriptableObject.getTopLevelScope(scope));
}
else {
RegExpProxy proxy = cx.getRegExpProxy();
if (proxy != null && proxy.isRegExp(jsThis)) {
return call(cx, fun, jsThis, args, scope);
}
}
}
}
}
else if (fun instanceof FunctionObject) {
FunctionObject fo = (FunctionObject) fun;
Member m = fo.method;
Class cl = m.getDeclaringClass();
String name = m.getName();
if (name.equals("exec")
&& (cx.getRegExpProxy() != null)
&& (cx.getRegExpProxy().isRegExp(jsThis)))
return call(cx, fun, jsThis, args, scope);
}
else // could've been <java>.XXX.exec() that was re-directed here
if (fun instanceof NativeJavaMethod)
return call(cx, fun, jsThis, args, scope);

Просмотреть файл

@ -53,7 +53,7 @@ import java.lang.reflect.Method;
* @author Brendan Eich
* @author Norris Boyd
*/
public class NativeRegExp extends ScriptableObject implements Function {
public class NativeRegExp extends IdScriptable implements Function {
public static final int GLOB = 0x1; // 'g' flag: global
public static final int FOLD = 0x2; // 'i' flag: fold
@ -66,31 +66,30 @@ public class NativeRegExp extends ScriptableObject implements Function {
private static final boolean debug = false;
public static void init(Scriptable scope)
throws PropertyException
{
public static void init(Context cx, Scriptable scope, boolean sealed) {
NativeRegExp proto = new NativeRegExp();
proto.prototypeFlag = true;
proto.activateIdMap(MAX_PROTOTYPE_ID);
proto.setSealFunctionsFlag(sealed);
proto.setFunctionParametrs(cx);
proto.setParentScope(scope);
proto.setPrototype(getObjectPrototype(scope));
String[] fns = { "compile", "toString", "exec", "test", "prefix" };
proto.defineFunctionProperties(fns, NativeRegExp.class,
ScriptableObject.DONTENUM);
String[] props =
{ "lastIndex", "source", "global", "ignoreCase", "multiline" };
int[] propAttrs =
{ ScriptableObject.PERMANENT,
ScriptableObject.PERMANENT | ScriptableObject.READONLY,
ScriptableObject.PERMANENT | ScriptableObject.READONLY,
ScriptableObject.PERMANENT | ScriptableObject.READONLY,
ScriptableObject.PERMANENT | ScriptableObject.READONLY };
for (int i=0; i < props.length; i++) {
proto.defineProperty(props[i], NativeRegExp.class, propAttrs[i]);
NativeRegExpCtor ctor = new NativeRegExpCtor();
ctor.setPrototype(getClassPrototype(scope, "Function"));
ctor.setParentScope(scope);
ctor.setImmunePrototypeProperty(proto);
if (sealed) {
proto.sealObject();
ctor.sealObject();
}
Scriptable ctor = NativeRegExpCtor.init(scope);
ctor.put("prototype", ctor, proto);
defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);
}
public NativeRegExp(Context cx, Scriptable scope, String source,
@ -170,16 +169,14 @@ public class NativeRegExp extends ScriptableObject implements Function {
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
return execSub(cx, this, args, scope, MATCH, this);
return execSub(cx, scope, args, MATCH);
}
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
return (Scriptable) call(cx, scope, null, args);
}
public static Scriptable compile(Context cx, Scriptable thisVal,
Object[] args, Function funObj) {
NativeRegExp thisObj = (NativeRegExp) thisVal; // XXX check cast
Scriptable compile(Context cx, Scriptable scope, Object[] args) {
if (args.length > 0 && args[0] instanceof NativeRegExp) {
if (args.length > 1 && args[1] != Undefined.instance) {
// report error
@ -187,23 +184,23 @@ public class NativeRegExp extends ScriptableObject implements Function {
cx, "TypeError",
"only one argument may be specified " +
"if the first argument is a RegExp object",
funObj);
scope);
}
NativeRegExp thatObj = (NativeRegExp) args[0];
thisObj.source = thatObj.source;
thisObj.lastIndex = thatObj.lastIndex;
thisObj.parenCount = thatObj.parenCount;
thisObj.flags = thatObj.flags;
thisObj.program = thatObj.program;
thisObj.ren = thatObj.ren;
return thisObj;
source = thatObj.source;
lastIndex = thatObj.lastIndex;
parenCount = thatObj.parenCount;
flags = thatObj.flags;
program = thatObj.program;
ren = thatObj.ren;
return this;
}
String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]);
String global = args.length > 1 && args[1] != Undefined.instance
? ScriptRuntime.toString(args[1])
: null;
thisObj.init(cx, funObj, s, global, false);
return thisObj;
init(cx, scope, s, global, false);
return this;
}
public String toString() {
@ -220,48 +217,6 @@ public class NativeRegExp extends ScriptableObject implements Function {
return buf.toString();
}
/**
* "lastIndex" property of RegExp instances (defined in prototype)
*/
public long getLastIndex() {
return (lastIndex & 0x0FFFFFFFFL);
}
/**
* "lastIndex" property of RegExp instances (defined in prototype)
*/
public void setLastIndex(int i) {
lastIndex = i;
}
/**
* "source" property of RegExp instances (defined in prototype)
*/
public String getSource() {
return source;
}
/**
* "global" property of RegExp instances (defined in prototype)
*/
public boolean getGlobal() {
return (flags & GLOB) != 0;
}
/**
* "ignoreCase" property of RegExp instances (defined in prototype)
*/
public boolean getIgnoreCase() {
return (flags & FOLD) != 0;
}
/**
* "multiline" property of RegExp instances (defined in prototype)
*/
public boolean getMultiline() {
return (flags & MULTILINE) != 0;
}
public NativeRegExp() {
}
@ -269,61 +224,48 @@ public class NativeRegExp extends ScriptableObject implements Function {
return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
}
private static Object execSub(Context cx, Scriptable thisObj,
Object[] args, Scriptable scopeObj,
int matchType, Function funObj) {
if (!(thisObj instanceof NativeRegExp)) {
Object[] errArgs = { ((NativeFunction) funObj).getFunctionName() };
throw NativeGlobal.constructError(
cx, "TypeError",
ScriptRuntime.getMessage(
"msg.incompat.call", errArgs),
scopeObj);
}
NativeRegExp re = (NativeRegExp) thisObj;
private Object execSub(Context cx, Scriptable scopeObj,
Object[] args, int matchType)
{
RegExpImpl reImpl = getImpl(cx);
String str;
if (args.length == 0) {
str = getImpl(cx).input;
str = reImpl.input;
if (str == null) {
Object[] errArgs = { re.toString() };
Object[] errArgs = { toString() };
throw NativeGlobal.constructError(
cx, "SyntaxError",
ScriptRuntime.getMessage
("msg.no.re.input.for", errArgs),
thisObj);
scopeObj);
}
} else {
str = ScriptRuntime.toString(args[0]);
}
int i;
if ((re.flags & GLOB) != 0) {
i = re.lastIndex;
} else {
i = 0;
}
int i = ((flags & GLOB) != 0) ? lastIndex : 0;
int indexp[] = { i };
Object rval = re.executeRegExp(scopeObj, str, indexp, matchType);
if ((re.flags & GLOB) != 0)
re.lastIndex = (rval == null || rval == Undefined.instance) ? 0 : indexp[0];
Object rval = executeRegExp(cx, scopeObj,
reImpl, str, indexp, matchType);
if ((flags & GLOB) != 0) {
lastIndex = (rval == null || rval == Undefined.instance)
? 0 : indexp[0];
}
return rval;
}
public static Object exec(Context cx, Scriptable thisObj,
Object[] args, Function funObj) {
return execSub(cx, thisObj, args, funObj, MATCH, funObj);
private Object exec(Context cx, Scriptable scopeObj, Object[] args) {
return execSub(cx, scopeObj, args, MATCH);
}
public static Object test(Context cx, Scriptable thisObj,
Object[] args, Function funObj) {
Object rval = execSub(cx, thisObj, args, funObj, TEST, funObj);
private Object test(Context cx, Scriptable scopeObj, Object[] args) {
Object rval = execSub(cx, scopeObj, args, TEST);
if (rval == null || !rval.equals(Boolean.TRUE))
rval = Boolean.FALSE;
return rval;
}
public static Object prefix(Context cx, Scriptable thisObj,
Object[] args, Function funObj) {
return execSub(cx, thisObj, args, funObj, PREFIX, funObj);
private Object prefix(Context cx, Scriptable scopeObj, Object[] args) {
return execSub(cx, scopeObj, args, PREFIX);
}
static final int JS_BITS_PER_BYTE = 8;
@ -1873,12 +1815,10 @@ public class NativeRegExp extends ScriptableObject implements Function {
/*
* indexp is assumed to be an array of length 1
*/
Object executeRegExp(Scriptable scopeObj, String str, int indexp[],
int matchType) {
Object executeRegExp(Context cx, Scriptable scopeObj, RegExpImpl res,
String str, int indexp[], int matchType)
{
NativeRegExp re = this;
Context cx = Context.getCurrentContext();
RegExpImpl res = getImpl(cx);
/*
* Initialize a CompilerState to minimize recursive argument traffic.
*/
@ -2028,9 +1968,180 @@ public class NativeRegExp extends ScriptableObject implements Function {
state.scope);
}
protected int getIdDefaultAttributes(int id) {
switch (id) {
case Id_lastIndex:
return ScriptableObject.PERMANENT;
case Id_source:
case Id_global:
case Id_ignoreCase:
case Id_multiline:
return ScriptableObject.PERMANENT | ScriptableObject.READONLY;
}
return super.getIdDefaultAttributes(id);
}
protected Object getIdValue(int id) {
switch (id) {
case Id_lastIndex: return wrap_long(0xffffffffL & lastIndex);
case Id_source: return source;
case Id_global: return wrap_boolean((flags & GLOB) != 0);
case Id_ignoreCase: return wrap_boolean((flags & FOLD) != 0);
case Id_multiline: return wrap_boolean((flags & MULTILINE) != 0);
}
return super.getIdValue(id);
}
protected void setIdValue(int id, Object value) {
if (id == Id_lastIndex) {
setLastIndex(ScriptRuntime.toInt32(value));
return;
}
super.setIdValue(id, value);
}
void setLastIndex(int value) {
lastIndex = value;
}
public int methodArity(int methodId) {
if (prototypeFlag) {
switch (methodId) {
case Id_compile: return 1;
case Id_toString: return 0;
case Id_exec: return 1;
case Id_test: return 1;
case Id_prefix: return 1;
}
}
return super.methodArity(methodId);
}
public Object execMethod(int methodId, IdFunction f, Context cx,
Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
if (prototypeFlag) {
switch (methodId) {
case Id_compile:
return realThis(thisObj, f, false).compile(cx, scope, args);
case Id_toString:
return realThis(thisObj, f, true).toString();
case Id_exec:
return realThis(thisObj, f, false).exec(cx, scope, args);
case Id_test:
return realThis(thisObj, f, false).test(cx, scope, args);
case Id_prefix:
return realThis(thisObj, f, false).prefix(cx, scope, args);
}
}
return super.execMethod(methodId, f, cx, scope, thisObj, args);
}
private NativeRegExp realThis(Scriptable thisObj, IdFunction f,
boolean readOnly)
{
while (!(thisObj instanceof NativeRegExp)) {
thisObj = nextInstanceCheck(thisObj, f, readOnly);
}
return (NativeRegExp)thisObj;
}
protected String getIdName(int id) {
switch (id) {
case Id_lastIndex: return "lastIndex";
case Id_source: return "source";
case Id_global: return "global";
case Id_ignoreCase: return "ignoreCase";
case Id_multiline: return "multiline";
}
if (prototypeFlag) {
switch (id) {
case Id_compile: return "compile";
case Id_toString: return "toString";
case Id_exec: return "exec";
case Id_test: return "test";
case Id_prefix: return "prefix";
}
}
return null;
}
protected int maxInstanceId() { return MAX_INSTANCE_ID; }
// #string_id_map#
private static final int
Id_lastIndex = 1,
Id_source = 2,
Id_global = 3,
Id_ignoreCase = 4,
Id_multiline = 5,
MAX_INSTANCE_ID = 5;
protected int mapNameToId(String s) {
int id;
// #generated# Last update: 2001-05-24 12:01:22 GMT+02:00
L0: { id = 0; String X = null; int c;
int s_length = s.length();
if (s_length==6) {
c=s.charAt(0);
if (c=='g') { X="global";id=Id_global; }
else if (c=='s') { X="source";id=Id_source; }
}
else if (s_length==9) {
c=s.charAt(0);
if (c=='l') { X="lastIndex";id=Id_lastIndex; }
else if (c=='m') { X="multiline";id=Id_multiline; }
}
else if (s_length==10) { X="ignoreCase";id=Id_ignoreCase; }
if (X!=null && X!=s && !X.equals(s)) id = 0;
}
// #/generated#
// #/string_id_map#
if (id != 0 || !prototypeFlag) { return id; }
// #string_id_map#
// #generated# Last update: 2001-05-24 12:01:22 GMT+02:00
L0: { id = 0; String X = null; int c;
L: switch (s.length()) {
case 4: c=s.charAt(0);
if (c=='e') { X="exec";id=Id_exec; }
else if (c=='t') { X="test";id=Id_test; }
break L;
case 6: X="prefix";id=Id_prefix; break L;
case 7: X="compile";id=Id_compile; break L;
case 8: X="toString";id=Id_toString; break L;
}
if (X!=null && X!=s && !X.equals(s)) id = 0;
}
// #/generated#
return id;
}
private static final int
Id_compile = MAX_INSTANCE_ID + 1,
Id_toString = MAX_INSTANCE_ID + 2,
Id_exec = MAX_INSTANCE_ID + 3,
Id_test = MAX_INSTANCE_ID + 4,
Id_prefix = MAX_INSTANCE_ID + 5,
MAX_PROTOTYPE_ID = MAX_INSTANCE_ID + 5;
// #/string_id_map#
private boolean prototypeFlag;
private String source; /* locked source string, sans // */
private int lastIndex; /* index after last match, for //g iterator */
private int parenCount; /* number of parenthesized submatches */
private int lastIndex; /* index after last match, for //g iterator */
private int parenCount; /* number of parenthesized submatches */
private byte flags; /* flags */
private byte[] program; /* regular expression bytecode */

Просмотреть файл

@ -52,83 +52,14 @@ import java.lang.reflect.Method;
*/
public class NativeRegExpCtor extends NativeFunction {
public static Scriptable init(Scriptable scope)
throws PropertyException
{
NativeRegExpCtor ctor = new NativeRegExpCtor();
ctor.functionName = "RegExp";
ctor.setPrototype(getClassPrototype(scope, "Function"));
ctor.setParentScope(scope);
String[] propNames = { "multiline", "input", "lastMatch",
"lastParen", "leftContext", "rightContext" };
String[] aliases = { "$*", "$_", "$&",
"$+", "$`", "$'" };
for (int i=0; i < propNames.length; i++)
ctor.defineProperty(propNames[i], NativeRegExpCtor.class,
ScriptableObject.DONTENUM);
for (int i=0; i < aliases.length; i++) {
StringBuffer buf = new StringBuffer("get");
buf.append(propNames[i]);
buf.setCharAt(3, Character.toUpperCase(propNames[i].charAt(0)));
Method[] getter = FunctionObject.findMethods(
NativeRegExpCtor.class,
buf.toString());
buf.setCharAt(0, 's');
Method[] setter = FunctionObject.findMethods(
NativeRegExpCtor.class,
buf.toString());
Method m = setter == null ? null : setter[0];
ctor.defineProperty(aliases[i], null, getter[0], m,
ScriptableObject.DONTENUM);
}
// We know that scope is a Scriptable object since we
// constrained the type on initStandardObjects.
ScriptableObject global = (ScriptableObject) scope;
global.defineProperty("RegExp", ctor, ScriptableObject.DONTENUM);
return ctor;
public NativeRegExpCtor() {
functionName = "RegExp";
}
public String getClassName() {
return "Function";
}
private int getDollarNumber(String s) {
// assumes s starts with '$'
if (s.length() != 2)
return 0;
char c = s.charAt(1);
if (c < '0' || '9' < c)
return 0;
return c - '0';
}
public boolean has(String id, Scriptable start) {
if (id != null && id.length() > 1 && id.charAt(0) == '$') {
if (getDollarNumber(id) != 0)
return true;
}
return super.has(id, start);
}
public Object get(String id, Scriptable start) {
if (id.length() > 1 && id.charAt(0) == '$') {
int dollar = getDollarNumber(id);
if (dollar != 0) {
dollar--;
RegExpImpl res = getImpl();
return res.getParenSubString(dollar).toString();
}
}
return super.get(id, start);
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
{
@ -142,53 +73,199 @@ public class NativeRegExpCtor extends NativeFunction {
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
NativeRegExp re = new NativeRegExp();
NativeRegExp.compile(cx, re, args, this);
re.compile(cx, scope, args);
re.setPrototype(getClassPrototype(scope, "RegExp"));
re.setParentScope(getParentScope());
return re;
}
public static boolean getMultiline(ScriptableObject obj) {
return getImpl().multiline;
}
public static void setMultiline(ScriptableObject obj, boolean b) {
getImpl().multiline = b;
}
public static String getInput(ScriptableObject obj) {
String s = getImpl().input;
return s == null ? "" : s;
}
public static void setInput(ScriptableObject obj, String s) {
getImpl().input = s;
}
public static String getLastMatch(ScriptableObject obj) {
SubString ss = getImpl().lastMatch;
return ss == null ? "" : ss.toString();
}
public static String getLastParen(ScriptableObject obj) {
SubString ss = getImpl().lastParen;
return ss == null ? "" : ss.toString();
}
public static String getLeftContext(ScriptableObject obj) {
SubString ss = getImpl().leftContext;
return ss == null ? "" : ss.toString();
}
public static String getRightContext(ScriptableObject obj) {
SubString ss = getImpl().rightContext;
return ss == null ? "" : ss.toString();
}
static RegExpImpl getImpl() {
Context cx = Context.getCurrentContext();
return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
}
protected int getIdDefaultAttributes(int id) {
int shifted = id - idBase;
if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
switch (shifted) {
case Id_multiline:
case Id_STAR:
case Id_input:
case Id_UNDERSCORE:
return PERMANENT;
}
return PERMANENT | READONLY;
}
return super.getIdDefaultAttributes(id);
}
private static String stringResult(Object obj) {
return (obj == null) ? "" : obj.toString();
}
protected Object getIdValue(int id) {
int shifted = id - idBase;
if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
RegExpImpl impl = getImpl();
switch (shifted) {
case Id_multiline:
case Id_STAR:
return wrap_boolean(impl.multiline);
case Id_input:
case Id_UNDERSCORE:
return stringResult(impl.input);
case Id_lastMatch:
case Id_AMPERSAND:
return stringResult(impl.lastMatch);
case Id_lastParen:
case Id_PLUS:
return stringResult(impl.lastParen);
case Id_leftContext:
case Id_BACK_QUOTE:
return stringResult(impl.leftContext);
case Id_rightContext:
case Id_QUOTE:
return stringResult(impl.rightContext);
}
// Must be one of $1..$9, convert to 0..8
int substring_number = shifted - DOLLAR_ID_BASE - 1;
return impl.getParenSubString(substring_number).toString();
}
return super.getIdValue(id);
}
protected void setIdValue(int id, Object value) {
switch (id - idBase) {
case Id_multiline:
case Id_STAR:
getImpl().multiline = ScriptRuntime.toBoolean(value);
return;
case Id_input:
case Id_UNDERSCORE:
getImpl().input = ScriptRuntime.toString(value);
return;
}
super.setIdValue(id, value);
}
protected String getIdName(int id) {
int shifted = id - idBase;
if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
switch (shifted) {
case Id_multiline: return "multiline";
case Id_STAR: return "$*";
case Id_input: return "input";
case Id_UNDERSCORE: return "$_";
case Id_lastMatch: return "lastMatch";
case Id_AMPERSAND: return "$&";
case Id_lastParen: return "lastParen";
case Id_PLUS: return "$+";
case Id_leftContext: return "leftContext";
case Id_BACK_QUOTE: return "$`";
case Id_rightContext: return "rightContext";
case Id_QUOTE: return "$'";
}
// Must be one of $1..$9, convert to 0..8
int substring_number = shifted - DOLLAR_ID_BASE - 1;
char[] buf = { '$', (char)('1' + substring_number) };
return new String(buf);
}
return super.getIdName(id);
}
protected int maxInstanceId() {
// Note: check for idBase == 0 can not be done in constructor,
// because IdScriptable calls maxInstanceId in its constructor
// before NativeRegExpCtor constructor gets chance to run any code
if (idBase == 0) { idBase = super.maxInstanceId(); }
return idBase + MAX_INSTANCE_ID;
}
// #string_id_map#
private static final int
Id_multiline = 1,
Id_STAR = 2, // #string=$*#
Id_input = 3,
Id_UNDERSCORE = 4, // #string=$_#
Id_lastMatch = 5,
Id_AMPERSAND = 6, // #string=$&#
Id_lastParen = 7,
Id_PLUS = 8, // #string=$+#
Id_leftContext = 9,
Id_BACK_QUOTE = 10, // #string=$`#
Id_rightContext = 11,
Id_QUOTE = 12, // #string=$'#
DOLLAR_ID_BASE = 12;
private static final int
Id_DOLLAR_1 = DOLLAR_ID_BASE + 1, // #string=$1#
Id_DOLLAR_2 = DOLLAR_ID_BASE + 2, // #string=$2#
Id_DOLLAR_3 = DOLLAR_ID_BASE + 3, // #string=$3#
Id_DOLLAR_4 = DOLLAR_ID_BASE + 4, // #string=$4#
Id_DOLLAR_5 = DOLLAR_ID_BASE + 5, // #string=$5#
Id_DOLLAR_6 = DOLLAR_ID_BASE + 6, // #string=$6#
Id_DOLLAR_7 = DOLLAR_ID_BASE + 7, // #string=$7#
Id_DOLLAR_8 = DOLLAR_ID_BASE + 8, // #string=$8#
Id_DOLLAR_9 = DOLLAR_ID_BASE + 9, // #string=$9#
MAX_INSTANCE_ID = DOLLAR_ID_BASE + 9;
protected int mapNameToId(String s) {
int id;
// #generated# Last update: 2001-05-24 16:09:31 GMT+02:00
L0: { id = 0; String X = null; int c;
L: switch (s.length()) {
case 2: switch (s.charAt(1)) {
case '&': if (s.charAt(0)=='$') {id=Id_AMPERSAND; break L0;} break L;
case '\'': if (s.charAt(0)=='$') {id=Id_QUOTE; break L0;} break L;
case '*': if (s.charAt(0)=='$') {id=Id_STAR; break L0;} break L;
case '+': if (s.charAt(0)=='$') {id=Id_PLUS; break L0;} break L;
case '1': if (s.charAt(0)=='$') {id=Id_DOLLAR_1; break L0;} break L;
case '2': if (s.charAt(0)=='$') {id=Id_DOLLAR_2; break L0;} break L;
case '3': if (s.charAt(0)=='$') {id=Id_DOLLAR_3; break L0;} break L;
case '4': if (s.charAt(0)=='$') {id=Id_DOLLAR_4; break L0;} break L;
case '5': if (s.charAt(0)=='$') {id=Id_DOLLAR_5; break L0;} break L;
case '6': if (s.charAt(0)=='$') {id=Id_DOLLAR_6; break L0;} break L;
case '7': if (s.charAt(0)=='$') {id=Id_DOLLAR_7; break L0;} break L;
case '8': if (s.charAt(0)=='$') {id=Id_DOLLAR_8; break L0;} break L;
case '9': if (s.charAt(0)=='$') {id=Id_DOLLAR_9; break L0;} break L;
case '_': if (s.charAt(0)=='$') {id=Id_UNDERSCORE; break L0;} break L;
case '`': if (s.charAt(0)=='$') {id=Id_BACK_QUOTE; break L0;} break L;
} break L;
case 5: X="input";id=Id_input; break L;
case 9: c=s.charAt(4);
if (c=='M') { X="lastMatch";id=Id_lastMatch; }
else if (c=='P') { X="lastParen";id=Id_lastParen; }
else if (c=='i') { X="multiline";id=Id_multiline; }
break L;
case 11: X="leftContext";id=Id_leftContext; break L;
case 12: X="rightContext";id=Id_rightContext; break L;
}
if (X!=null && X!=s && !X.equals(s)) id = 0;
}
// #/generated#
// #/string_id_map#
return (id != 0) ? idBase + id : super.mapNameToId(s);
}
private static int idBase;
}

Просмотреть файл

@ -46,16 +46,6 @@ public class RegExpImpl implements RegExpProxy {
parens = new Vector(9);
}
protected Object executeRegExp(Object regExp, Scriptable scopeObj,
String str, int indexp[], int matchType)
{
if (regExp instanceof NativeRegExp)
return ((NativeRegExp) regExp).executeRegExp(scopeObj, str,
indexp, matchType);
return null;
}
public boolean isRegExp(Object obj) {
return obj instanceof NativeRegExp;
}
@ -74,7 +64,8 @@ public class RegExpImpl implements RegExpProxy {
mdata.optarg = 1;
mdata.mode = GlobData.GLOB_MATCH;
mdata.parent = ScriptableObject.getTopLevelScope(funObj);
Object rval = matchOrReplace(cx, thisObj, args, funObj, mdata, false);
Object rval = matchOrReplace(cx, thisObj, args, funObj,
this, mdata, false);
return mdata.arrayobj == null ? rval : mdata.arrayobj;
}
@ -86,7 +77,7 @@ public class RegExpImpl implements RegExpProxy {
mdata.optarg = 1;
mdata.mode = GlobData.GLOB_SEARCH;
mdata.parent = ScriptableObject.getTopLevelScope(funObj);
return matchOrReplace(cx, thisObj, args, funObj, mdata, false);
return matchOrReplace(cx, thisObj, args, funObj, this, mdata, false);
}
public Object replace(Context cx, Scriptable thisObj, Object[] args,
@ -112,7 +103,8 @@ public class RegExpImpl implements RegExpProxy {
rdata.length = 0;
rdata.index = 0;
rdata.leftIndex = 0;
Object val = matchOrReplace(cx, thisObj, args, funObj, rdata, true);
Object val = matchOrReplace(cx, thisObj, args, funObj,
this, rdata, true);
char[] charArray;
if (rdata.charArray == null) {
@ -121,12 +113,12 @@ public class RegExpImpl implements RegExpProxy {
return rdata.str;
}
int leftlen = this.leftContext.length;
int length = leftlen + rdata.findReplen(this);
int length = leftlen + rdata.findReplen(cx, this);
charArray = new char[length];
SubString leftContext = this.leftContext;
System.arraycopy(leftContext.charArray, leftContext.index,
charArray, 0, leftlen);
rdata.doReplace(this, charArray, leftlen);
rdata.doReplace(cx, this, charArray, leftlen);
rdata.charArray = charArray;
rdata.length = length;
}
@ -147,6 +139,7 @@ public class RegExpImpl implements RegExpProxy {
*/
private static Object matchOrReplace(Context cx, Scriptable thisObj,
Object[] args, Function funObj,
RegExpImpl reImpl,
GlobData data, boolean forceFlat)
throws JavaScriptException
{
@ -154,7 +147,6 @@ public class RegExpImpl implements RegExpProxy {
String str = ScriptRuntime.toString(thisObj);
data.str = str;
RegExpImpl reImpl = RegExpImpl.getRegExpImpl(cx);
Scriptable scope = ScriptableObject.getTopLevelScope(funObj);
if (args.length == 0)
@ -179,7 +171,8 @@ public class RegExpImpl implements RegExpProxy {
int[] indexp = { 0 };
Object result = null;
if (data.mode == GlobData.GLOB_SEARCH) {
result = re.executeRegExp(funObj, str, indexp, NativeRegExp.TEST);
result = re.executeRegExp(cx, funObj, reImpl,
str, indexp, NativeRegExp.TEST);
if (result != null && result.equals(Boolean.TRUE))
result = new Integer(reImpl.leftContext.length);
else
@ -187,10 +180,11 @@ public class RegExpImpl implements RegExpProxy {
} else if (data.global) {
re.setLastIndex(0);
for (int count = 0; indexp[0] <= str.length(); count++) {
result = re.executeRegExp(funObj, str, indexp, NativeRegExp.TEST);
result = re.executeRegExp(cx, funObj, reImpl,
str, indexp, NativeRegExp.TEST);
if (result == null || !result.equals(Boolean.TRUE))
break;
data.doGlobal(funObj, count);
data.doGlobal(cx, funObj, count, reImpl);
if (reImpl.lastMatch.length == 0) {
if (indexp[0] == str.length())
break;
@ -198,8 +192,10 @@ public class RegExpImpl implements RegExpProxy {
}
}
} else {
result = re.executeRegExp(funObj, str, indexp,
((data.mode == GlobData.GLOB_REPLACE) ? NativeRegExp.TEST : NativeRegExp.MATCH));
result = re.executeRegExp(cx, funObj, reImpl, str, indexp,
((data.mode == GlobData.GLOB_REPLACE)
? NativeRegExp.TEST
: NativeRegExp.MATCH));
}
return result;
@ -215,6 +211,7 @@ public class RegExpImpl implements RegExpProxy {
int length = target.length();
int result;
Context cx = Context.getCurrentContext();
int version = cx.getLanguageVersion();
NativeRegExp re = (NativeRegExp) reObj;
again:
@ -222,8 +219,9 @@ public class RegExpImpl implements RegExpProxy {
/* JS1.2 deviated from Perl by never matching at end of string. */
int ipsave = ip[0]; // reuse ip to save object creation
ip[0] = i;
if (re.executeRegExp(funObj, target, ip, NativeRegExp.TEST) != Boolean.TRUE)
{
Object ret = re.executeRegExp(cx, funObj, this, target, ip,
NativeRegExp.TEST);
if (ret != Boolean.TRUE) {
// Mismatch: ensure our caller advances i past end of string.
ip[0] = ipsave;
matchlen[0] = 1;
@ -276,10 +274,6 @@ public class RegExpImpl implements RegExpProxy {
return result;
}
static RegExpImpl getRegExpImpl(Context cx) {
return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
}
/**
* Analog of REGEXP_PAREN_SUBSTRING in C jsregexp.h.
* Assumes zero-based; i.e., for $3, i==2
@ -306,7 +300,8 @@ abstract class GlobData {
static final int GLOB_REPLACE = 2;
static final int GLOB_SEARCH = 3;
abstract void doGlobal(Function funObj, int count)
abstract void doGlobal(Context cx, Function funObj, int count,
RegExpImpl reImpl)
throws JavaScriptException;
byte mode; /* input: return index, match object, or void */
@ -323,13 +318,13 @@ class MatchData extends GlobData {
/*
* Analog of match_glob() in jsstr.c
*/
void doGlobal(Function funObj, int count) throws JavaScriptException {
void doGlobal(Context cx, Function funObj, int count, RegExpImpl reImpl)
throws JavaScriptException
{
MatchData mdata;
Object v;
mdata = this;
Context cx = Context.getCurrentContext();
RegExpImpl reImpl = RegExpImpl.getRegExpImpl(cx);
if (arrayobj == null) {
Scriptable s = ScriptableObject.getTopLevelScope(funObj);
arrayobj = ScriptRuntime.newObject(cx, s, "Array", null);
@ -352,13 +347,11 @@ class ReplaceData extends GlobData {
/*
* Analog of replace_glob() in jsstr.c
*/
void doGlobal(Function funObj, int count)
void doGlobal(Context cx, Function funObj, int count, RegExpImpl reImpl)
throws JavaScriptException
{
ReplaceData rdata = this;
Context cx = Context.getCurrentContext();
RegExpImpl reImpl = RegExpImpl.getRegExpImpl(cx);
SubString lc = reImpl.leftContext;
char[] leftArray = lc.charArray;
@ -366,7 +359,7 @@ class ReplaceData extends GlobData {
int leftlen = reImpl.lastMatch.index - leftIndex;
rdata.leftIndex = reImpl.lastMatch.index + reImpl.lastMatch.length;
int replen = findReplen(reImpl);
int replen = findReplen(cx, reImpl);
int growth = leftlen + replen;
char[] charArray;
if (rdata.charArray != null) {
@ -382,15 +375,14 @@ class ReplaceData extends GlobData {
rdata.index += growth;
System.arraycopy(leftArray, leftIndex, charArray, index, leftlen);
index += leftlen;
doReplace(reImpl, charArray, index);
doReplace(cx, reImpl, charArray, index);
}
static SubString dollarStr = new SubString("$");
static SubString interpretDollar(RegExpImpl res, char[] da, int dp,
int bp, int[] skip)
static SubString interpretDollar(Context cx, RegExpImpl res,
char[] da, int dp, int bp, int[] skip)
{
Context cx = Context.getCurrentContext();
char[] ca;
int cp;
char dc;
@ -401,8 +393,8 @@ class ReplaceData extends GlobData {
throw new RuntimeException();
if ((cx.getLanguageVersion() != Context.VERSION_DEFAULT)
&& (cx.getLanguageVersion() <= Context.VERSION_1_4))
if (dp > bp && da[dp-1] == '\\')
return null;
if (dp > bp && da[dp-1] == '\\')
return null;
/* Interpret all Perl match-induced dollar variables. */
dc = da[dp+1];
@ -450,16 +442,16 @@ class ReplaceData extends GlobData {
return res.lastParen;
case '`':
if (cx.getLanguageVersion() == Context.VERSION_1_2) {
/*
* JS1.2 imitated the Perl4 bug where left context at each step
* in an iterative use of a global regexp started from last match,
* not from the start of the target string. But Perl4 does start
* $` at the beginning of the target string when it is used in a
* substitution, so we emulate that special case here.
*/
/*
* JS1.2 imitated the Perl4 bug where left context at each step
* in an iterative use of a global regexp started from last match,
* not from the start of the target string. But Perl4 does start
* $` at the beginning of the target string when it is used in a
* substitution, so we emulate that special case here.
*/
res.leftContext.index = 0;
res.leftContext.length = res.lastMatch.index;
}
}
return res.leftContext;
case '\'':
return res.rightContext;
@ -472,13 +464,12 @@ class ReplaceData extends GlobData {
* the result parameter sizep is the return value (errors are
* propagated with exceptions).
*/
int findReplen(RegExpImpl reImpl)
int findReplen(Context cx, RegExpImpl reImpl)
throws JavaScriptException
{
if (lambda != null) {
// invoke lambda function with args lastMatch, $1, $2, ... $n,
// leftContext.length, whole string.
Context cx = Context.getCurrentContext();
Vector parens = reImpl.parens;
int parenCount = parens.size();
Object[] args = new Object[parenCount + 3];
@ -508,7 +499,7 @@ class ReplaceData extends GlobData {
continue;
}
int[] skip = { 0 };
SubString sub = interpretDollar(reImpl, this.repstr, dp,
SubString sub = interpretDollar(cx, reImpl, this.repstr, dp,
bp, skip);
if (sub != null) {
replen += sub.length - skip[0];
@ -523,7 +514,7 @@ class ReplaceData extends GlobData {
/**
* Analog of do_replace in jsstr.c
*/
void doReplace(RegExpImpl regExpImpl, char[] charArray,
void doReplace(Context cx, RegExpImpl regExpImpl, char[] charArray,
int arrayIndex)
{
int cp = 0;
@ -539,7 +530,7 @@ class ReplaceData extends GlobData {
arrayIndex += len;
cp = dp;
int[] skip = { 0 };
SubString sub = interpretDollar(regExpImpl, da,
SubString sub = interpretDollar(cx, regExpImpl, da,
dp, bp, skip);
if (sub != null) {
len = sub.length;