diff --git a/README.md b/README.md index d5ca09388a..8974a7b124 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ Rhino is an implementation of JavaScript in Java. ## This repository is a fork of rhino for [fixRTM](https://github.com/anatawa12/fixRTM) Here is the list of difference from the original Rhino: +- NativeString will search String's method. +- toString for function return real function source code. +- Packages.some.Class.class should return java.lang.Class Object. ## License diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java index 97e5b05ba0..618de2470e 100644 --- a/src/org/mozilla/javascript/NativeFunction.java +++ b/src/org/mozilla/javascript/NativeFunction.java @@ -23,6 +23,10 @@ public final void initScriptFunction(Context cx, Scriptable scope) ScriptRuntime.setFunctionProtoAndParent(this, scope); } + public String decompileRaw() { + return null; + } + /** * @param indent How much to indent the decompiled result * @@ -31,6 +35,11 @@ public final void initScriptFunction(Context cx, Scriptable scope) @Override final String decompile(int indent, int flags) { + if (indent == 0 && flags == 0) { + String raw = decompileRaw(); + if (raw != null) + return raw; + } String encodedSource = getEncodedSource(); if (encodedSource == null) { return super.decompile(indent, flags); diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index 39316f8923..d23e4c2b9c 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -33,6 +33,8 @@ public class NativeJavaClass extends NativeJavaObject implements Function // Special property for getting the underlying Java class object. static final String javaClassPropertyName = "__javaObject__"; + static final String javaClassPropertyName2 = "class"; + public NativeJavaClass() { } @@ -58,7 +60,8 @@ public String getClassName() { @Override public boolean has(String name, Scriptable start) { - return members.has(name, true) || javaClassPropertyName.equals(name); + return members.has(name, true) || javaClassPropertyName.equals(name) + || javaClassPropertyName2.equals(name); } @Override @@ -84,7 +87,7 @@ public Object get(String name, Scriptable start) { Scriptable scope = ScriptableObject.getTopLevelScope(start); WrapFactory wrapFactory = cx.getWrapFactory(); - if (javaClassPropertyName.equals(name)) { + if (javaClassPropertyName.equals(name) || javaClassPropertyName2.equals(name)) { return wrapFactory.wrap(cx, scope, javaObject, ScriptRuntime.ClassClass); } diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index ca5d3b7eb4..989dbbf0be 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -11,7 +11,11 @@ import java.text.Collator; import java.text.Normalizer; +import java.util.Arrays; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; +import java.util.function.Function; import org.mozilla.javascript.regexp.NativeRegExp; @@ -126,6 +130,65 @@ protected void fillConstructorProperties(IdFunctionObject ctor) super.fillConstructorProperties(ctor); } + @Override + public Object get(String name, Scriptable start) { + Object result = super.get(name, start); + if (result != NOT_FOUND) return result; + if (StringMethod_names.contains(name)) return NOT_FOUND; + // get ctx + Context ctx = Context.getCurrentContext(); + if (ctx == null) return NOT_FOUND; + // get by object + result = ctx.getWrapFactory().wrapNewObject(ctx, ScriptableObject.getTopLevelScope(start), "") + .get(name, start); + if (result instanceof NativeJavaMethod) { + return new WrappedNativeFunction((NativeJavaMethod) result, + (obj) -> obj instanceof NativeString ? ((NativeString) obj).toCharSequence().toString() : null); + } + return result; + } + + private static final Set StringMethod_names = new HashSet<>(Arrays.asList( + "charAt", "charCodeAt", "codePointAt", "concat", "includes", "endsWith", "indexOf", "lastIndexOf", + "localeCompare", "match", "normalize", "padEnd", "padStart", "quote", "repeat", "replace", "search", + "slice", "split", "startsWith", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", + "toLowerCase", "toSource", "toUpperCase", "trim", "trimStart", "trimLeft", "trimEnd", + "trimRight", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", + "link", "small", "strike", "sub", "sup", "toString", "valueOf" + )); + + private static class WrappedNativeFunction extends NativeJavaMethod { + private final NativeJavaMethod javaMethod; + private final Function unwrap; + + WrappedNativeFunction(NativeJavaMethod javaMethod, Function unwrap) { + super(javaMethod.methods, javaMethod.getFunctionName()); + this.javaMethod = javaMethod; + this.unwrap = unwrap; + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (thisObj == null) + return javaMethod.call(cx, scope, thisObj, args); + + Object unwrapped = unwrap.apply(thisObj); + if (unwrapped instanceof Scriptable) + return javaMethod.call(cx, scope, (Scriptable) unwrapped, args); + if (unwrapped == null) + return javaMethod.call(cx, scope, thisObj, args); + + thisObj = cx.getWrapFactory().wrapNewObject(cx, ScriptableObject.getTopLevelScope(scope), unwrapped); + + return javaMethod.call(cx, scope, thisObj, args); + } + + @Override + public Object getDefaultValue(Class typeHint) { + return javaMethod.getDefaultValue(typeHint); + } + } + @Override protected void initPrototypeId(int id) { diff --git a/src/org/mozilla/javascript/optimizer/ClassCompiler.java b/src/org/mozilla/javascript/optimizer/ClassCompiler.java index d7c78ad376..441b5ca986 100644 --- a/src/org/mozilla/javascript/optimizer/ClassCompiler.java +++ b/src/org/mozilla/javascript/optimizer/ClassCompiler.java @@ -156,6 +156,10 @@ public Object[] compileToClassFiles(String source, } Codegen codegen = new Codegen(); + + // to know real source code + codegen.sourceString = source; + codegen.setMainMethodClass(mainMethodClassName); byte[] scriptClassBytes = codegen.compileToClassFile(compilerEnv, scriptClassName, diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index f1f2b79f96..815b263b48 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -743,6 +743,9 @@ private void generateFunctionInit(ClassFileWriter cfw, cfw.stopMethod((short)3); } + // to kwnow real source code + String sourceString; + private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encodedSource) { @@ -766,13 +769,20 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, final int Do_getParamOrVarName = 3; final int Do_getEncodedSource = 4; final int Do_getParamOrVarConst = 5; - final int SWITCH_COUNT = 6; + // add Do_decompileRaw + final int Do_decompileRaw = 6; + final int SWITCH_COUNT = 7; for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) { if (methodIndex == Do_getEncodedSource && encodedSource == null) { continue; } + // add condition for Do_decompileRaw + if (methodIndex == Do_decompileRaw && sourceString == null) { + continue; + } + // Generate: // prologue; // switch over function id to implement function-specific action @@ -811,6 +821,15 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, ACC_PUBLIC); cfw.addPush(encodedSource); break; + + // add for Do_decompileRaw + case Do_decompileRaw: + methodLocals = 1; // Only this + cfw.startMethod("decompileRaw", "()Ljava/lang/String;", + ACC_PUBLIC); + cfw.addPush(sourceString); + break; + default: throw Kit.codeBug(); } @@ -953,6 +972,19 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, cfw.add(ByteCode.ARETURN); break; + // add for Do_decompileRaw + case Do_decompileRaw: + // Push number source start and end + // to prepare for encodedSource.substring(start, end) + cfw.addPush(n.getAbsolutePosition()); + cfw.addPush(n.getAbsolutePosition() + n.getLength()); + cfw.addInvoke(ByteCode.INVOKEVIRTUAL, + "java/lang/String", + "substring", + "(II)Ljava/lang/String;"); + cfw.add(ByteCode.ARETURN); + break; + default: throw Kit.codeBug(); }