diff --git a/js/rhino/src/org/mozilla/javascript/BaseFunction.java b/js/rhino/src/org/mozilla/javascript/BaseFunction.java index 506a8462ee8e..47021dc0a594 100644 --- a/js/rhino/src/org/mozilla/javascript/BaseFunction.java +++ b/js/rhino/src/org/mozilla/javascript/BaseFunction.java @@ -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; } diff --git a/js/rhino/src/org/mozilla/javascript/IdScriptable.java b/js/rhino/src/org/mozilla/javascript/IdScriptable.java index 05fe9542737a..585fa147d0bb 100644 --- a/js/rhino/src/org/mozilla/javascript/IdScriptable.java +++ b/js/rhino/src/org/mozilla/javascript/IdScriptable.java @@ -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; diff --git a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java index c2226a31b390..ec6af4b20f51 100644 --- a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java @@ -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 .XXX.exec() that was re-directed here if (fun instanceof NativeJavaMethod) return call(cx, fun, jsThis, args, scope); diff --git a/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExp.java b/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExp.java index 96490a9c8482..e69b1a1a951b 100644 --- a/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -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 */ diff --git a/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java b/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java index d7f8bd6eed9e..2c3540af4b76 100644 --- a/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java +++ b/js/rhino/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java @@ -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; } diff --git a/js/rhino/src/org/mozilla/javascript/regexp/RegExpImpl.java b/js/rhino/src/org/mozilla/javascript/regexp/RegExpImpl.java index b027c079fc1e..77a080502fce 100644 --- a/js/rhino/src/org/mozilla/javascript/regexp/RegExpImpl.java +++ b/js/rhino/src/org/mozilla/javascript/regexp/RegExpImpl.java @@ -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;