This commit is contained in:
Lana Steuck 2014-11-06 15:13:08 -08:00
commit f0dc68f1aa
201 changed files with 3297 additions and 1225 deletions

View File

@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class EvalFile {
public static void main(String[] args) throws Exception {
public static void main(final String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
final ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("nashorn");
final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}

View File

@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class EvalScript {
public static void main(String[] args) throws Exception {
public static void main(final String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
final ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("nashorn");
final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}

View File

@ -29,22 +29,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class InvokeScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
String script = "function hello(name) { print('Hello, ' + name); }";
final String script = "function hello(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
final Invocable inv = (Invocable) engine;
// invoke the global function named "hello"
inv.invokeFunction("hello", "Scripting!!" );

View File

@ -29,26 +29,29 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class InvokeScriptMethod {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String. This code defines a script object 'obj'
// with one method called 'hello'.
String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
final String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
final Invocable inv = (Invocable) engine;
// get script object on which we want to call the method
Object obj = engine.get("obj");
final Object obj = engine.get("obj");
// invoke the method named "hello" on the script object "obj"
inv.invokeMethod(obj, "hello", "Script Method !!" );

View File

@ -29,12 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;
@SuppressWarnings("javadoc")
public class MultiScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("x", "hello");
// print global variable "x"
@ -42,9 +47,9 @@ public class MultiScopes {
// the above line prints "hello"
// Now, pass a different script context
ScriptContext newContext = new SimpleScriptContext();
final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
final Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope
engineScope.put("x", "world");

View File

@ -29,28 +29,31 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class RunnableImpl {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
String script = "function run() { print('run called'); }";
final String script = "function run() { print('run called'); }";
// evaluate script
engine.eval(script);
Invocable inv = (Invocable) engine;
final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
Runnable r = inv.getInterface(Runnable.class);
final Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
final Thread th = new Thread(r);
th.start();
th.join();
}

View File

@ -29,31 +29,34 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class RunnableImplObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
final String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
Object obj = engine.get("obj");
final Object obj = engine.get("obj");
Invocable inv = (Invocable) engine;
final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
Runnable r = inv.getInterface(obj, Runnable.class);
final Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
final Thread th = new Thread(r);
th.start();
th.join();
}

View File

@ -29,15 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.script.*;
import java.io.*;
import java.io.File;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@SuppressWarnings("javadoc")
public class ScriptVars {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
public static void main(final String[] args) throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
File f = new File("test.txt");
final File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);

View File

@ -97,6 +97,7 @@ import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.support.AutoDiscovery;
import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
@ -105,6 +106,7 @@ import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
import jdk.internal.dynalink.support.LinkerServicesImpl;
import jdk.internal.dynalink.support.TypeConverterFactory;
import jdk.internal.dynalink.support.TypeUtilities;
/**
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
@ -115,7 +117,6 @@ import jdk.internal.dynalink.support.TypeConverterFactory;
* @author Attila Szegedi
*/
public class DynamicLinkerFactory {
/**
* Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}.
*/
@ -130,6 +131,7 @@ public class DynamicLinkerFactory {
private boolean syncOnRelink = false;
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
private GuardedInvocationFilter prelinkFilter;
private MethodTypeConversionStrategy autoConversionStrategy;
/**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@ -258,6 +260,29 @@ public class DynamicLinkerFactory {
this.prelinkFilter = prelinkFilter;
}
/**
* Sets an object representing the conversion strategy for automatic type conversions. After
* {@link TypeConverterFactory#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has
* applied all custom conversions to a method handle, it still needs to effect
* {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
* can usually be automatically applied as per
* {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
* However, sometimes language runtimes will want to customize even those conversions for their own call
* sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
* ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
* automatic conversion strategy, that can deal with null values. Note that when the strategy's
* {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
* is invoked, the custom language conversions will already have been applied to the method handle, so by
* design the difference between the handle's current method type and the desired final type will always
* only be ones that can be subjected to method invocation conversions. The strategy also doesn't need to
* invoke a final {@code MethodHandle.asType()} as the converter factory will do that as the final step.
* @param autoConversionStrategy the strategy for applying method invocation conversions for the linker
* created by this factory.
*/
public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
this.autoConversionStrategy = autoConversionStrategy;
}
/**
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
* the pre-link filter.
@ -324,8 +349,9 @@ public class DynamicLinkerFactory {
prelinkFilter = new DefaultPrelinkFilter();
}
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink,
unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {

View File

@ -287,10 +287,21 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
*/
private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
if(CallerSensitiveDetector.isCallerSensitive(m)) {
// Method has @CallerSensitive annotation
return new CallerSensitiveDynamicMethod(m);
}
// Method has no @CallerSensitive annotation
final MethodHandle mh;
try {
mh = unreflectSafely(m);
} catch (final IllegalAccessError e) {
// java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
// marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
return new CallerSensitiveDynamicMethod(m);
}
// Proceed with non-caller sensitive
final Member member = (Member)m;
return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
}
/**

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file, and Oracle licenses the original version of this file under the BSD
* license:
*/
/*
Copyright 2014 Attila Szegedi
Licensed under both the Apache License, Version 2.0 (the "Apache License")
and the BSD License (the "BSD License"), with licensee being free to
choose either of the two at their discretion.
You may not use this file except in compliance with either the Apache
License or the BSD License.
If you choose to use this file in compliance with the Apache License, the
following notice applies to you:
You may obtain a copy of the Apache License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
If you choose to use this file in compliance with the BSD License, the
following notice applies to you:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jdk.internal.dynalink.linker;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
/**
* Interface for objects representing a strategy for converting a method handle to a new type.
*/
public interface MethodTypeConversionStrategy {
/**
* Converts a method handle to a new type.
* @param target target method handle
* @param newType new type
* @return target converted to the new type.
*/
public MethodHandle asType(final MethodHandle target, final MethodType newType);
}

View File

@ -97,6 +97,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardedTypeConversion;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
/**
* A factory for type converters. This class is the main implementation behind the
@ -109,6 +110,7 @@ public class TypeConverterFactory {
private final GuardingTypeConverterFactory[] factories;
private final ConversionComparator[] comparators;
private final MethodTypeConversionStrategy autoConversionStrategy;
private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
@Override
@ -177,8 +179,24 @@ public class TypeConverterFactory {
* Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
*
* @param factories the {@link GuardingTypeConverterFactory} instances to compose.
* @param autoConversionStrategy conversion strategy for automatic type conversions. After
* {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom
* conversions to a method handle, it still needs to effect
* {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
* can usually be automatically applied as per
* {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
* However, sometimes language runtimes will want to customize even those conversions for their own call
* sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
* ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
* automatic conversion strategy, that can deal with null values. Note that when the strategy's
* {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
* is invoked, the custom language conversions will already have been applied to the method handle, so by
* design the difference between the handle's current method type and the desired final type will always
* only be ones that can be subjected to method invocation conversions. Can be null, in which case no
* custom strategy is employed.
*/
public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories) {
public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories,
final MethodTypeConversionStrategy autoConversionStrategy) {
final List<GuardingTypeConverterFactory> l = new LinkedList<>();
final List<ConversionComparator> c = new LinkedList<>();
for(final GuardingTypeConverterFactory factory: factories) {
@ -189,20 +207,24 @@ public class TypeConverterFactory {
}
this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
this.comparators = c.toArray(new ConversionComparator[c.size()]);
this.autoConversionStrategy = autoConversionStrategy;
}
/**
* Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
* {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
* parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
* wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
* it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
* provided by {@link GuardingTypeConverterFactory} implementations.
* parameters. For all conversions that are not a JLS method invocation conversion it'll insert
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
* provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation
* conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first
* if an automatic conversion strategy was specified in the
* {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply
* {@link MethodHandle#asType(MethodType)} for any remaining conversions.
*
* @param handle target method handle
* @param fromType the types of source arguments
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
* {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
* {@link GuardingTypeConverterFactory} produced type converters as filters.
*/
@ -246,8 +268,12 @@ public class TypeConverterFactory {
}
}
// Take care of automatic conversions
return newHandle.asType(fromType);
// Give change to automatic conversion strategy, if one is present.
final MethodHandle autoConvertedHandle =
autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle;
// Do a final asType for any conversions that remain.
return autoConvertedHandle.asType(fromType);
}
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {

View File

@ -520,4 +520,13 @@ public class TypeUtilities {
public static Class<?> getWrapperType(final Class<?> primitiveType) {
return WRAPPER_TYPES.get(primitiveType);
}
/**
* Returns true if the passed type is a wrapper for a primitive type.
* @param type the examined type
* @return true if the passed type is a wrapper for a primitive type.
*/
public static boolean isWrapperType(final Class<?> type) {
return PRIMITIVE_TYPES.containsKey(type);
}
}

View File

@ -27,8 +27,8 @@ package jdk.nashorn.internal;
/**
* Class that exposes the current state of asserts.
*
*/
@SuppressWarnings("all")
public final class AssertsEnabled {
private static boolean assertsEnabled = false;
static {

View File

@ -29,6 +29,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
import java.lang.invoke.MethodType;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
@ -93,6 +94,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
private final Deque<MethodType> callSiteTypes = new ArrayDeque<>();
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
/**
@ -118,86 +121,108 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return context.getLogger(this.getClass());
}
@SuppressWarnings("serial")
private static class TransformFailedException extends RuntimeException {
TransformFailedException(final FunctionNode fn, final String message) {
super(massageURL(fn.getSource().getURL()) + '.' + fn.getName() + " => " + message, null, false, false);
}
}
@SuppressWarnings("serial")
private static class AppliesFoundException extends RuntimeException {
AppliesFoundException() {
super("applies_found", null, false, false);
}
}
private static final AppliesFoundException HAS_APPLIES = new AppliesFoundException();
private boolean hasApplies(final FunctionNode functionNode) {
try {
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterCallNode(final CallNode callNode) {
if (isApply(callNode)) {
throw HAS_APPLIES;
}
return true;
}
});
} catch (final AppliesFoundException e) {
return true;
}
log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
return false; // no applies
}
/**
* Arguments may only be used as args to the apply. Everything else is disqualified
* We cannot control arguments if they escape from the method and go into an unknown
* scope, thus we are conservative and treat any access to arguments outside the
* apply call as a case of "we cannot apply the optimization".
*
* @return true if arguments escape
*/
private boolean argumentsEscape(final FunctionNode functionNode) {
@SuppressWarnings("serial")
final UnsupportedOperationException uoe = new UnsupportedOperationException() {
@Override
public synchronized Throwable fillInStackTrace() {
return null;
}
};
private void checkValidTransform(final FunctionNode functionNode) {
final Set<Expression> argumentsFound = new HashSet<>();
final Deque<Set<Expression>> stack = new ArrayDeque<>();
//ensure that arguments is only passed as arg to apply
try {
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean isCurrentArg(final Expression expr) {
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
}
private boolean isArguments(final Expression expr) {
if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
argumentsFound.add(expr);
//ensure that arguments is only passed as arg to apply
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean isCurrentArg(final Expression expr) {
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
}
private boolean isArguments(final Expression expr) {
if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
argumentsFound.add(expr);
return true;
}
return false;
}
private boolean isParam(final String name) {
for (final IdentNode param : functionNode.getParameters()) {
if (param.getName().equals(name)) {
return true;
}
return false;
}
private boolean isParam(final String name) {
for (final IdentNode param : functionNode.getParameters()) {
if (param.getName().equals(name)) {
return true;
}
}
return false;
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
throw uoe; //avoid filling in stack trace
}
return identNode;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
final Set<Expression> callArgs = new HashSet<>();
if (isApply(callNode)) {
final List<Expression> argList = callNode.getArgs();
if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
throw new UnsupportedOperationException();
}
callArgs.addAll(callNode.getArgs());
}
stack.push(callArgs);
return true;
}
@Override
public Node leaveCallNode(final CallNode callNode) {
stack.pop();
return callNode;
}
});
} catch (final UnsupportedOperationException e) {
if (!argumentsFound.isEmpty()) {
log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
return false;
}
return true; //bad
}
return false;
@Override
public Node leaveIdentNode(final IdentNode identNode) {
if (isParam(identNode.getName())) {
throw new TransformFailedException(lc.getCurrentFunction(), "parameter: " + identNode.getName());
}
// it's OK if 'argument' occurs as the current argument of an apply
if (isArguments(identNode) && !isCurrentArg(identNode)) {
throw new TransformFailedException(lc.getCurrentFunction(), "is 'arguments': " + identNode.getName());
}
return identNode;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
final Set<Expression> callArgs = new HashSet<>();
if (isApply(callNode)) {
final List<Expression> argList = callNode.getArgs();
if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
throw new TransformFailedException(lc.getCurrentFunction(), "argument pattern not matched: " + argList);
}
callArgs.addAll(callNode.getArgs());
}
stack.push(callArgs);
return true;
}
@Override
public Node leaveCallNode(final CallNode callNode) {
stack.pop();
return callNode;
}
});
}
@Override
@ -224,12 +249,14 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
log.fine("Transformed ",
callNode,
" from apply to call => ",
newCallNode,
" in ",
DebugLogger.quote(lc.getCurrentFunction().getName()));
if (log.isEnabled()) {
log.fine("Transformed ",
callNode,
" from apply to call => ",
newCallNode,
" in ",
DebugLogger.quote(lc.getCurrentFunction().getName()));
}
return newCallNode;
}
@ -237,12 +264,12 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return callNode;
}
private boolean pushExplodedArgs(final FunctionNode functionNode) {
private void pushExplodedArgs(final FunctionNode functionNode) {
int start = 0;
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
if (actualCallSiteType == null) {
return false;
throw new TransformFailedException(lc.getCurrentFunction(), "No callsite type");
}
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
@ -264,8 +291,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
}
callSiteTypes.push(actualCallSiteType);
explodedArguments.push(newParams);
return true;
}
@Override
@ -288,11 +315,30 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return false;
}
if (argumentsEscape(functionNode)) {
if (!hasApplies(functionNode)) {
return false;
}
return pushExplodedArgs(functionNode);
if (log.isEnabled()) {
log.info("Trying to specialize apply to call in '",
functionNode.getName(),
"' params=",
functionNode.getParameters(),
" id=",
functionNode.getId(),
" source=",
massageURL(functionNode.getSource().getURL()));
}
try {
checkValidTransform(functionNode);
pushExplodedArgs(functionNode);
} catch (final TransformFailedException e) {
log.info("Failure: ", e.getMessage());
return false;
}
return true;
}
/**
@ -300,8 +346,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
* @return true if successful, false otherwise
*/
@Override
public Node leaveFunctionNode(final FunctionNode functionNode0) {
FunctionNode newFunctionNode = functionNode0;
public Node leaveFunctionNode(final FunctionNode functionNode) {
FunctionNode newFunctionNode = functionNode;
final String functionName = newFunctionNode.getName();
if (changed.contains(newFunctionNode.getId())) {
@ -310,17 +356,18 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
setParameters(lc, explodedArguments.peek());
if (log.isEnabled()) {
log.info("Successfully specialized apply to call in '",
log.info("Success: ",
massageURL(newFunctionNode.getSource().getURL()),
'.',
functionName,
" params=",
explodedArguments.peek(),
"' id=",
newFunctionNode.getId(),
" source=",
newFunctionNode.getSource().getURL());
" params=",
callSiteTypes.peek());
}
}
callSiteTypes.pop();
explodedArguments.pop();
return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
@ -331,4 +378,15 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
}
private static String massageURL(final URL url) {
if (url == null) {
return "<null>";
}
final String str = url.toString();
final int slash = str.lastIndexOf('/');
if (slash == -1) {
return str;
}
return str.substring(slash + 1);
}
}

View File

@ -615,7 +615,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
static final TypeBounds INT = exact(Type.INT);
static final TypeBounds NUMBER = exact(Type.NUMBER);
static final TypeBounds OBJECT = exact(Type.OBJECT);
static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
@ -3569,7 +3568,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
} else {
// Non-optimistic, non-FP +. Allow it to overflow.
operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest),
Type.OBJECT);
forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
}
loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
@ -3856,12 +3856,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
operandBounds = numericBounds;
} else {
final boolean isOptimistic = isValid(getProgramPoint());
if(isOptimistic) {
if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
} else if(node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
// Non-optimistic division must always take double arguments as its result must also be
// double.
operandBounds = TypeBounds.NUMBER;
} else {
// Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
@ -3897,7 +3893,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private static boolean isRhsZero(final BinaryNode binaryNode) {
final Expression rhs = binaryNode.rhs();
return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode)rhs).getValue());
return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode<?>)rhs).getValue());
}
private void loadBIT_XOR(final BinaryNode binaryNode) {

View File

@ -31,7 +31,6 @@ import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
@ -250,7 +249,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
static Type getTypeForSlotDescriptor(final char typeDesc) {
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
switch(typeDesc) {
switch (typeDesc) {
case 'I':
case 'i':
return Type.INT;

View File

@ -389,6 +389,7 @@ public final class Compiler implements Loggable {
* @param continuationEntryPoints continuation entry points for restof method
* @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
*/
@SuppressWarnings("unused")
public Compiler(
final Context context,
final ScriptEnvironment env,

View File

@ -28,7 +28,6 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@ -236,12 +235,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private byte conversions;
void recordConversion(final LvarType from, final LvarType to) {
switch(from) {
switch (from) {
case UNDEFINED:
return;
case INT:
case BOOLEAN:
switch(to) {
switch (to) {
case LONG:
recordConversion(I2L);
return;
@ -256,7 +255,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return;
}
case LONG:
switch(to) {
switch (to) {
case DOUBLE:
recordConversion(L2D);
return;
@ -1425,6 +1424,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
* @param symbol the symbol representing the variable
* @param type the type
*/
@SuppressWarnings("unused")
private void setType(final Symbol symbol, final LvarType type) {
if(getLocalVariableTypeOrNull(symbol) == type) {
return;

View File

@ -1591,7 +1591,7 @@ public class MethodEmitter implements Emitter {
/**
* Abstraction for performing a conditional jump of any type
*
* @see MethodEmitter.Condition
* @see Condition
*
* @param cond the condition to test
* @param trueLabel the destination label is condition is true
@ -2217,6 +2217,10 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
if (name.length() > LARGE_STRING_THRESHOLD) { // use getIndex for extremely long names
return load(name).dynamicGetIndex(valueType, flags, isMethod);
}
debug("dynamic_get", name, valueType, getProgramPoint(flags));
Type type = valueType;
@ -2240,9 +2244,14 @@ public class MethodEmitter implements Emitter {
* @param name name of property
* @param flags call site flags
*/
void dynamicSet(final String name, final int flags) {
assert !isOptimistic(flags);
debug("dynamic_set", name, peekType());
void dynamicSet(final String name, final int flags) {
if (name.length() > LARGE_STRING_THRESHOLD) { // use setIndex for extremely long names
load(name).swap().dynamicSetIndex(flags);
return;
}
assert !isOptimistic(flags);
debug("dynamic_set", name, peekType());
Type type = peekType();
if (type.isObject() || type.isBoolean()) { //promote strings to objects etc

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.MethodEmitter.LARGE_STRING_THRESHOLD;
import java.util.HashMap;
/**
@ -66,27 +68,28 @@ public class Namespace {
}
/**
* Create a uniqueName name in the namespace in the form base$n where n varies
* .
* @param base Base of name. Base will be returned if uniqueName.
* Create a uniqueName name in the namespace in the form base$n where n varies.
* Also truncates very long names that would otherwise break ASM.
*
* @param base Base of name. Base will be returned if uniqueName.
* @return Generated uniqueName name.
*/
public String uniqueName(final String base) {
final String truncatedBase = base.length() > LARGE_STRING_THRESHOLD ? base.substring(0, LARGE_STRING_THRESHOLD) : base;
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
final HashMap<String, Integer> namespaceDirectory = namespace.directory;
final Integer counter = namespaceDirectory.get(base);
final Integer counter = namespaceDirectory.get(truncatedBase);
if (counter != null) {
final int count = counter + 1;
namespaceDirectory.put(base, count);
return base + '-' + count;
namespaceDirectory.put(truncatedBase, count);
return truncatedBase + '-' + count;
}
}
directory.put(base, 0);
directory.put(truncatedBase, 0);
return base;
return truncatedBase;
}
@Override

View File

@ -323,9 +323,11 @@ public final class OptimisticTypesPersistence {
* per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
* for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
* timestamp of the most recent .class file.
* @return
*
* @return digest of currently running nashorn
* @throws Exception if digest could not be created
*/
private static String getVersionDirName() throws Exception {
public static String getVersionDirName() throws Exception {
final URL url = OptimisticTypesPersistence.class.getResource("");
final String protocol = url.getProtocol();
if (protocol.equals("jar")) {

View File

@ -55,6 +55,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.runtime.JSType;
/**
* Type class: INT
@ -230,19 +231,21 @@ class IntType extends BitwiseType {
@Override
public Type div(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer division in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
if (programPoint == INVALID_PROGRAM_POINT) {
JSType.DIV_ZERO.invoke(method);
} else {
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer remainder in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
if (programPoint == INVALID_PROGRAM_POINT) {
JSType.REM_ZERO.invoke(method);
} else {
method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
}
return INT;
}

View File

@ -170,19 +170,21 @@ class LongType extends BitwiseType {
@Override
public Type div(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer division in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
if (programPoint == INVALID_PROGRAM_POINT) {
JSType.DIV_ZERO_LONG.invoke(method);
} else {
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
}
return LONG;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
// Never perform non-optimistic integer remainder in JavaScript.
assert programPoint != INVALID_PROGRAM_POINT;
method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
if (programPoint == INVALID_PROGRAM_POINT) {
JSType.REM_ZERO_LONG.invoke(method);
} else {
method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
}
return LONG;
}

View File

@ -356,7 +356,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
final int pp = input.readInt();
final int typeChar = input.readByte();
final Type type;
switch(typeChar) {
switch (typeChar) {
case 'L': type = Type.OBJECT; break;
case 'D': type = Type.NUMBER; break;
case 'J': type = Type.LONG; break;
@ -376,13 +376,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
}
private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> c = INTERNAL_TYPE_CACHE;
jdk.internal.org.objectweb.asm.Type itype = c.get(type);
if (itype != null) {
return itype;
}
itype = jdk.internal.org.objectweb.asm.Type.getType(type);
cache.put(type, itype);
c.put(type, itype);
return itype;
}
@ -1155,6 +1155,10 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
return type;
}
/**
* Read resolve
* @return resolved type
*/
protected final Object readResolve() {
return Type.typeFor(clazz);
}

View File

@ -28,7 +28,6 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.internal.dynalink.CallSiteDescriptor;
@ -44,6 +43,9 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
/**
* ArrayBufferView, es6 class or TypedArray implementation
*/
@ScriptClass("ArrayBufferView")
public abstract class ArrayBufferView extends ScriptObject {
private final NativeArrayBuffer buffer;
@ -71,6 +73,13 @@ public abstract class ArrayBufferView extends ScriptObject {
setArray(data);
}
/**
* Constructor
*
* @param buffer underlying NativeArrayBuffer
* @param byteOffset byte offset for buffer
* @param elementLength element length in bytes
*/
protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
this(buffer, byteOffset, elementLength, Global.instance());
}
@ -89,22 +98,42 @@ public abstract class ArrayBufferView extends ScriptObject {
return factory().bytesPerElement;
}
/**
* Buffer getter as per spec
* @param self ArrayBufferView instance
* @return buffer
*/
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static Object buffer(final Object self) {
return ((ArrayBufferView)self).buffer;
}
/**
* Buffer offset getter as per spec
* @param self ArrayBufferView instance
* @return buffer offset
*/
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteOffset(final Object self) {
return ((ArrayBufferView)self).byteOffset;
}
/**
* Byte length getter as per spec
* @param self ArrayBufferView instance
* @return array buffer view length in bytes
*/
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteLength(final Object self) {
final ArrayBufferView view = (ArrayBufferView)self;
return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement();
}
/**
* Length getter as per spec
* @param self ArrayBufferView instance
* @return length in elements
*/
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int length(final Object self) {
return ((ArrayBufferView)self).elementLength();
@ -119,15 +148,29 @@ public abstract class ArrayBufferView extends ScriptObject {
return ((TypedArrayData<?>)getArray()).getElementLength();
}
/**
* Factory class for byte ArrayBufferViews
*/
protected static abstract class Factory {
final int bytesPerElement;
final int maxElementLength;
/**
* Constructor
*
* @param bytesPerElement number of bytes per element for this buffer
*/
public Factory(final int bytesPerElement) {
this.bytesPerElement = bytesPerElement;
this.maxElementLength = Integer.MAX_VALUE / bytesPerElement;
}
/**
* Factory method
*
* @param elementLength number of elements
* @return new ArrayBufferView
*/
public final ArrayBufferView construct(final int elementLength) {
if (elementLength > maxElementLength) {
throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength));
@ -135,15 +178,47 @@ public abstract class ArrayBufferView extends ScriptObject {
return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength);
}
public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength);
/**
* Factory method
*
* @param buffer underlying buffer
* @param byteOffset byte offset
* @param elementLength number of elements
*
* @return new ArrayBufferView
*/
public abstract ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength);
public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end);
/**
* Factory method for array data
*
* @param nb underlying nativebuffer
* @param start start element
* @param end end element
*
* @return new array data
*/
public abstract TypedArrayData<?> createArrayData(final ByteBuffer nb, final int start, final int end);
/**
* Get the class name for this type of buffer
*
* @return class name
*/
public abstract String getClassName();
}
/**
* Get the factor for this kind of buffer
* @return Factory
*/
protected abstract Factory factory();
/**
* Get the prototype for this ArrayBufferView
* @param global global instance
* @return prototype
*/
protected abstract ScriptObject getPrototype(final Global global);
@Override
@ -151,10 +226,23 @@ public abstract class ArrayBufferView extends ScriptObject {
return factory().getClassName();
}
/**
* Check if this array contains floats
* @return true if float array (or double)
*/
protected boolean isFloatArray() {
return false;
}
/**
* Inheritable constructor implementation
*
* @param newObj is this a new constructor
* @param args arguments
* @param factory factory
*
* @return new ArrayBufferView
*/
protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) {
final Object arg0 = args.length != 0 ? args[0] : 0;
final ArrayBufferView dest;
@ -200,6 +288,15 @@ public abstract class ArrayBufferView extends ScriptObject {
return dest;
}
/**
* Inheritable implementation of set, if no efficient implementation is available
*
* @param self ArrayBufferView instance
* @param array array
* @param offset0 array offset
*
* @return result of setter
*/
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
final ArrayBufferView dest = (ArrayBufferView)self;
final int length;
@ -244,6 +341,15 @@ public abstract class ArrayBufferView extends ScriptObject {
return (int)(length & Integer.MAX_VALUE);
}
/**
* Implementation of subarray if no efficient override exists
*
* @param self ArrayBufferView instance
* @param begin0 begin index
* @param end0 end index
*
* @return sub array
*/
protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) {
final ArrayBufferView arrayView = (ArrayBufferView)self;
final int byteOffset = arrayView.byteOffset;

View File

@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
@ -41,7 +42,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import jdk.internal.dynalink.linker.GuardedInvocation;
@ -54,7 +54,6 @@ import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
@ -438,9 +437,6 @@ public final class Global extends ScriptObject implements Scope {
this.scontext = scontext;
}
// global constants for this global - they can be replaced with MethodHandle.constant until invalidated
private static AtomicReference<GlobalConstants> gcsInstance = new AtomicReference<>();
@Override
protected Context getContext() {
return context;
@ -470,11 +466,6 @@ public final class Global extends ScriptObject implements Scope {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
//we can only share one instance of Global constants between globals, or we consume way too much
//memory - this is good enough for most programs
while (gcsInstance.get() == null) {
gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
}
}
/**
@ -492,15 +483,6 @@ public final class Global extends ScriptObject implements Scope {
return self instanceof Global? (Global)self : instance();
}
/**
* Return the global constants map for fields that
* can be accessed as MethodHandle.constant
* @return constant map
*/
public static GlobalConstants getConstants() {
return gcsInstance.get();
}
/**
* Check if we have a Global instance
* @return true if one exists
@ -559,16 +541,16 @@ public final class Global extends ScriptObject implements Scope {
* as well as our extension builtin objects like "Java", "JSAdapter" as properties
* of the global scope object.
*
* @param engine ScriptEngine to initialize
* @param eng ScriptEngine to initialize
*/
public void initBuiltinObjects(final ScriptEngine engine) {
public void initBuiltinObjects(final ScriptEngine eng) {
if (this.builtinObject != null) {
// already initialized, just return
return;
}
this.engine = engine;
init(engine);
this.engine = eng;
init(eng);
}
/**
@ -1717,7 +1699,7 @@ public final class Global extends ScriptObject implements Scope {
return func;
}
private void init(final ScriptEngine engine) {
private void init(final ScriptEngine eng) {
assert Context.getGlobal() == this : "this global is not set as current";
final ScriptEnvironment env = getContext().getEnv();
@ -1835,7 +1817,7 @@ public final class Global extends ScriptObject implements Scope {
addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, arguments);
}
if (engine != null) {
if (eng != null) {
// default file name
addOwnProperty(ScriptEngine.FILENAME, Attribute.NOT_ENUMERABLE, null);
// __noSuchProperty__ hook for ScriptContext search of missing variables

View File

@ -26,7 +26,6 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.nio.ByteBuffer;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
@ -34,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
@ -137,6 +137,19 @@ public final class NativeArrayBuffer extends ScriptObject {
return ((NativeArrayBuffer)self).getByteLength();
}
/**
* Returns true if an object is an ArrayBufferView
*
* @param self self
* @param obj object to check
*
* @return true if obj is an ArrayBufferView
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isView(final Object self, final Object obj) {
return obj instanceof ArrayBufferView;
}
/**
* Slice function
* @param self native array buffer

View File

@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -572,7 +573,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
try {
return ((CharSequence)self).charAt(pos);
} catch (final IndexOutOfBoundsException e) {
throw new ClassCastException();
throw new ClassCastException(); //invalid char, out of bounds, force relink
}
}
@ -1380,7 +1381,6 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
* sequence and that we are in range
*/
private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
@Override
@ -1389,7 +1389,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
//check that it's a char sequence or throw cce
final CharSequence cs = (CharSequence)self;
//check that the index, representable as an int, is inside the array
final int intIndex = JSType.toInteger(request.getArguments()[1]);
final int intIndex = JSType.toInteger(request.getArguments()[2]);
return intIndex >= 0 && intIndex < cs.length(); //can link
} catch (final ClassCastException | IndexOutOfBoundsException e) {
//fallthru

View File

@ -141,9 +141,8 @@ class ParserContext {
return breakable;
}
return null;
} else {
return getBreakable();
}
return getBreakable();
}
/**

View File

@ -56,7 +56,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
/**
* Returns a single flag
* @param flag
* @param flag flag
* @return A single flag
*/
protected int getFlag(final int flag) {
@ -64,7 +64,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
}
/**
* @param flag
* @param flag flag
* @return the new flags
*/
@Override
@ -82,7 +82,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
}
/**
* @param statements
* @param statements statements
*/
@Override
public void setStatements(final List<Statement> statements) {

View File

@ -41,6 +41,7 @@ import java.security.PrivilegedExceptionAction;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
@ -102,7 +103,7 @@ public abstract class CodeStore implements Loggable {
} catch (final AccessControlException e) {
context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
}
final CodeStore store = new DirectoryCodeStore();
final CodeStore store = new DirectoryCodeStore(context);
store.initLogger(context);
return store;
}
@ -210,32 +211,34 @@ public abstract class CodeStore implements Loggable {
/**
* Constructor
*
* @param context the current context
* @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore() throws IOException {
this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
public DirectoryCodeStore(final Context context) throws IOException {
this(context, Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
}
/**
* Constructor
*
* @param context the current context
* @param path directory to store code in
* @param readOnly is this a read only code store
* @param minSize minimum file size for caching scripts
* @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
this.dir = checkDirectory(path, readOnly);
public DirectoryCodeStore(final Context context, final String path, final boolean readOnly, final int minSize) throws IOException {
this.dir = checkDirectory(path, context.getEnv(), readOnly);
this.readOnly = readOnly;
this.minSize = minSize;
}
private static File checkDirectory(final String path, final boolean readOnly) throws IOException {
private static File checkDirectory(final String path, final ScriptEnvironment env, final boolean readOnly) throws IOException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
@Override
public File run() throws IOException {
final File dir = new File(path).getAbsoluteFile();
final File dir = new File(path, getVersionDir(env)).getAbsoluteFile();
if (readOnly) {
if (!dir.exists() || !dir.isDirectory()) {
throw new IOException("Not a directory: " + dir.getPath());
@ -257,6 +260,15 @@ public abstract class CodeStore implements Loggable {
}
}
private static String getVersionDir(final ScriptEnvironment env) throws IOException {
try {
final String versionDir = OptimisticTypesPersistence.getVersionDirName();
return env._optimistic_types ? versionDir + "_opt" : versionDir;
} catch (final Exception e) {
throw new IOException(e);
}
}
@Override
public StoredScript load(final Source source, final String functionKey) {
if (source.getLength() < minSize) {

View File

@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -777,7 +776,7 @@ final class CompiledFunction {
// Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
// this function doesn't have a callee parameter.
final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
final MethodType ct = type.parameterType(0) == ScriptFunction.class ?
type :
type.insertParameterTypes(0, ScriptFunction.class);
final OptimismInfo currentOptInfo = optimismInfo;
@ -788,29 +787,29 @@ final class CompiledFunction {
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
FunctionNode fn = effectiveOptInfo.reparse();
final boolean serialized = effectiveOptInfo.isSerialized();
final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of
if (!shouldRecompile) {
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
// recompiled a deoptimized version for an inner invocation.
// We still need to do the rest of from the beginning
logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
}
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
log.info("Reusable IR generated");
// compile the rest of the function, and install it
log.info("Generating and installing bytecode from reusable IR...");
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL);
if (effectiveOptInfo.data.usePersistentCodeCache()) {
final RecompilableScriptFunctionData data = effectiveOptInfo.data;
final int functionNodeId = data.getFunctionNodeId();
final TypeMap typeMap = data.typeMap(callSiteType);
final TypeMap typeMap = data.typeMap(ct);
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
compiler.persistClassInfo(cacheKey, normalFn);
@ -871,6 +870,7 @@ final class CompiledFunction {
private SwitchPoint optimisticAssumptions;
private final DebugLogger log;
@SuppressWarnings("unused")
OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) {
this.data = data;
this.log = data.getLogger();

View File

@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@ -60,6 +61,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
@ -262,6 +264,10 @@ public final class Context {
// persistent code store
private CodeStore codeStore;
// A factory for linking global properties as constant method handles. It is created when the first Global
// is created, and invalidated forever once the second global is created.
private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
/**
* Get the current global scope
* @return the current global scope
@ -293,7 +299,10 @@ public final class Context {
assert getGlobal() != global;
//same code can be cached between globals, then we need to invalidate method handle constants
if (global != null) {
Global.getConstants().invalidateAll();
final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
if (globalConstants != null) {
globalConstants.invalidateAll();
}
}
currentGlobal.set(global);
}
@ -528,6 +537,15 @@ public final class Context {
return classFilter;
}
/**
* Returns the factory for constant method handles for global properties. The returned factory can be
* invalidated if this Context has more than one Global.
* @return the factory for constant method handles for global properties.
*/
GlobalConstants getGlobalConstants() {
return globalConstantsRef.get();
}
/**
* Get the error manager for this context
* @return error manger
@ -1016,9 +1034,32 @@ public final class Context {
* @return the global script object
*/
public Global newGlobal() {
createOrInvalidateGlobalConstants();
return new Global(this);
}
private void createOrInvalidateGlobalConstants() {
for (;;) {
final GlobalConstants currentGlobalConstants = getGlobalConstants();
if (currentGlobalConstants != null) {
// Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
// with more than one Global, as the constant method handle linkages it creates create a coupling
// between the Global and the call sites in the compiled code.
currentGlobalConstants.invalidateForever();
return;
}
final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
// First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
// for this Context.
return;
}
// If we reach here, then we started out as the first invocation, but another concurrent invocation won the
// CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
}
}
/**
* Initialize given global scope object.
*
@ -1057,12 +1098,19 @@ public final class Context {
* @return current global's context
*/
static Context getContextTrusted() {
return ((ScriptObject)Context.getGlobal()).getContext();
return getContext(getGlobal());
}
static Context getContextTrustedOrNull() {
final Global global = Context.getGlobal();
return global == null ? null : ((ScriptObject)global).getContext();
return global == null ? null : getContext(global);
}
private static Context getContext(final Global global) {
// We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
// In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
// virtual invocation do its thing.
return ((ScriptObject)global).getContext();
}
/**
@ -1150,9 +1198,8 @@ public final class Context {
StoredScript storedScript = null;
FunctionNode functionNode = null;
// We only use the code store here if optimistic types are disabled. With optimistic types,
// code is stored per function in RecompilableScriptFunctionData.
// TODO: This should really be triggered by lazy compilation, not optimistic types.
// We only use the code store here if optimistic types are disabled. With optimistic types, initial compilation
// just creates a thin wrapper, and actual code is stored per function in RecompilableScriptFunctionData.
final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;

View File

@ -96,15 +96,17 @@ public final class ECMAException extends NashornException {
// If thrown object is an Error or sub-object like TypeError, then
// an ECMAException object has been already initialized at constructor.
if (thrown instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject)thrown;
final Object exception = getException(sobj);
final Object exception = getException((ScriptObject)thrown);
if (exception instanceof ECMAException) {
// copy over file name, line number and column number.
final ECMAException ee = (ECMAException)exception;
ee.setFileName(fileName);
ee.setLineNumber(line);
ee.setColumnNumber(column);
return ee;
// Make sure exception has correct thrown reference because that's what will end up getting caught.
if (ee.getThrown() == thrown) {
// copy over file name, line number and column number.
ee.setFileName(fileName);
ee.setLineNumber(line);
ee.setColumnNumber(column);
return ee;
}
}
}
@ -154,7 +156,11 @@ public final class ECMAException extends NashornException {
* @return a {@link ECMAException}
*/
public static Object getException(final ScriptObject errObj) {
return errObj.get(ECMAException.EXCEPTION_PROPERTY);
// Exclude inherited properties that may belong to errors in the prototype chain.
if (errObj.hasOwnProperty(ECMAException.EXCEPTION_PROPERTY)) {
return errObj.get(ECMAException.EXCEPTION_PROPERTY);
}
return null;
}
/**

View File

@ -31,12 +31,14 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
@ -50,7 +52,7 @@ import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
/**
* Each global owns one of these. This is basically table of accessors
* Each context owns one of these. This is basically table of accessors
* for global properties. A global constant is evaluated to a MethodHandle.constant
* for faster access and to avoid walking to proto chain looking for it.
*
@ -67,12 +69,19 @@ import jdk.nashorn.internal.runtime.logging.Logger;
* reregister the switchpoint. Set twice or more - don't try again forever, or we'd
* just end up relinking our way into megamorphisism.
*
* Also it has to be noted that this kind of linking creates a coupling between a Global
* and the call sites in compiled code belonging to the Context. For this reason, the
* linkage becomes incorrect as soon as the Context has more than one Global. The
* {@link #invalidateForever()} is invoked by the Context to invalidate all linkages and
* turn off the functionality of this object as soon as the Context's {@link Context#newGlobal()} is invoked
* for second time.
*
* We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
* a receiver guard on the constant getter, but it currently leaks memory and its benefits
* have not yet been investigated property.
*
* As long as all Globals share the same constant instance, we need synchronization
* whenever we access the instance.
* As long as all Globals in a Context share the same GlobalConstants instance, we need synchronization
* whenever we access it.
*/
@Logger(name="const")
public final class GlobalConstants implements Loggable {
@ -82,7 +91,7 @@ public final class GlobalConstants implements Loggable {
* Script objects require a receiver guard, which is memory intensive, so this is currently
* disabled. We might implement a weak reference based approach to this later.
*/
private static final boolean GLOBAL_ONLY = true;
public static final boolean GLOBAL_ONLY = true;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@ -98,6 +107,8 @@ public final class GlobalConstants implements Loggable {
*/
private final Map<String, Access> map = new HashMap<>();
private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
/**
* Constructor - used only by global
* @param log logger, or null if none
@ -216,10 +227,34 @@ public final class GlobalConstants implements Loggable {
* the same class for a new global, but the builtins and global scoped variables
* will have changed.
*/
public synchronized void invalidateAll() {
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
for (final Access acc : map.values()) {
acc.invalidateUncounted();
public void invalidateAll() {
if (!invalidatedForever.get()) {
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
synchronized (this) {
for (final Access acc : map.values()) {
acc.invalidateUncounted();
}
}
}
}
/**
* To avoid an expensive global guard "is this the same global", similar to the
* receiver guard on the ScriptObject level, we invalidate all getters when the
* second Global is created by the Context owning this instance. After this
* method is invoked, this GlobalConstants instance will both invalidate all the
* switch points it produced, and it will stop handing out new method handles
* altogether.
*/
public void invalidateForever() {
if (invalidatedForever.compareAndSet(false, true)) {
log.info("New global created - invalidating all constant callsites.");
synchronized (this) {
for (final Access acc : map.values()) {
acc.invalidateForever();
}
map.clear();
}
}
}
@ -251,7 +286,7 @@ public final class GlobalConstants implements Loggable {
return obj;
}
private synchronized Access getOrCreateSwitchPoint(final String name) {
private Access getOrCreateSwitchPoint(final String name) {
Access acc = map.get(name);
if (acc != null) {
return acc;
@ -267,9 +302,13 @@ public final class GlobalConstants implements Loggable {
* @param name name of property
*/
void delete(final String name) {
final Access acc = map.get(name);
if (acc != null) {
acc.invalidateForever();
if (!invalidatedForever.get()) {
synchronized (this) {
final Access acc = map.get(name);
if (acc != null) {
acc.invalidateForever();
}
}
}
}
@ -313,45 +352,45 @@ public final class GlobalConstants implements Loggable {
*
* @return null if failed to set up constant linkage
*/
synchronized GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
if (invalidatedForever.get() || (GLOBAL_ONLY && !isGlobalSetter(receiver, find))) {
return null;
}
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Access acc = getOrCreateSwitchPoint(name);
synchronized (this) {
final Access acc = getOrCreateSwitchPoint(name);
if (log.isEnabled()) {
log.fine("Trying to link constant SETTER ", acc);
}
if (!acc.mayRetry()) {
if (log.isEnabled()) {
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
log.fine("Trying to link constant SETTER ", acc);
}
return null;
if (!acc.mayRetry() || invalidatedForever.get()) {
if (log.isEnabled()) {
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
}
return null;
}
if (acc.hasBeenInvalidated()) {
log.info("New chance for " + acc);
acc.newSwitchPoint();
}
assert !acc.hasBeenInvalidated();
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
final MethodHandle target = inv.getInvocation();
final Class<?> receiverType = target.type().parameterType(0);
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
}
assert acc.mayRetry();
if (acc.hasBeenInvalidated()) {
log.info("New chance for " + acc);
acc.newSwitchPoint();
}
assert !acc.hasBeenInvalidated();
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
final MethodHandle target = inv.getInvocation();
final Class<?> receiverType = target.type().parameterType(0);
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
}
/**
@ -380,11 +419,11 @@ public final class GlobalConstants implements Loggable {
*
* @return resulting getter, or null if failed to create constant
*/
synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
// Only use constant getter for fast scope access, because the receiver may change between invocations
// for slow-scope and non-scope callsites.
// Also return null for user accessor properties as they may have side effects.
if (!NashornCallSiteDescriptor.isFastScope(desc)
if (invalidatedForever.get() || !NashornCallSiteDescriptor.isFastScope(desc)
|| (GLOBAL_ONLY && !find.getOwner().isGlobal())
|| find.getProperty() instanceof UserAccessorProperty) {
return null;
@ -395,51 +434,53 @@ public final class GlobalConstants implements Loggable {
final Class<?> retType = desc.getMethodType().returnType();
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Access acc = getOrCreateSwitchPoint(name);
synchronized (this) {
final Access acc = getOrCreateSwitchPoint(name);
log.fine("Starting to look up object value " + name);
final Object c = find.getObjectValue();
log.fine("Starting to look up object value " + name);
final Object c = find.getObjectValue();
if (log.isEnabled()) {
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
}
if (acc.hasBeenInvalidated() || acc.guardFailed()) {
if (log.isEnabled()) {
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
}
return null;
}
final MethodHandle cmh = constantGetter(c);
if (acc.hasBeenInvalidated() || acc.guardFailed() || invalidatedForever.get()) {
if (log.isEnabled()) {
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
}
return null;
}
MethodHandle mh;
MethodHandle guard;
final MethodHandle cmh = constantGetter(c);
if (isOptimistic) {
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
//widen return type - this is pessimistic, so it will always work
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
MethodHandle mh;
MethodHandle guard;
if (isOptimistic) {
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
//widen return type - this is pessimistic, so it will always work
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
} else {
//immediately invalidate - we asked for a too wide constant as a narrower one
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
}
} else {
//immediately invalidate - we asked for a too wide constant as a narrower one
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
//pessimistic return type filter
mh = Lookup.filterReturnType(cmh, retType);
}
} else {
//pessimistic return type filter
mh = Lookup.filterReturnType(cmh, retType);
}
if (find.getOwner().isGlobal()) {
guard = null;
} else {
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
}
if (find.getOwner().isGlobal()) {
guard = null;
} else {
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
}
if (log.isEnabled()) {
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
}
if (log.isEnabled()) {
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
}
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
}
}
}

View File

@ -150,6 +150,12 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
/** Div zero wrapper for integer division that handles (0/0)|0 == 0 */
public static final Call DIV_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", int.class, int.class, int.class);
/** Mod zero wrapper for integer division that handles (0%0)|0 == 0 */
public static final Call REM_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", int.class, int.class, int.class);
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
@ -174,6 +180,12 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
/** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
public static final Call DIV_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
/** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
public static final Call REM_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
@ -1485,6 +1497,28 @@ public enum JSType {
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
}
/**
* Implements int division but allows {@code x / 0} to be represented as 0. Basically equivalent to
* {@code (x / y)|0} JavaScript expression (division of two ints coerced to int).
* @param x the dividend
* @param y the divisor
* @return the result
*/
public static int divZero(final int x, final int y) {
return y == 0 ? 0 : x / y;
}
/**
* Implements int remainder but allows {@code x % 0} to be represented as 0. Basically equivalent to
* {@code (x % y)|0} JavaScript expression (remainder of two ints coerced to int).
* @param x the dividend
* @param y the divisor
* @return the remainder
*/
public static int remZero(final int x, final int y) {
return y == 0 ? 0 : x % y;
}
/**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*
@ -1528,6 +1562,28 @@ public enum JSType {
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
}
/**
* Implements long division but allows {@code x / 0} to be represented as 0. Useful when division of two longs
* is coerced to long.
* @param x the dividend
* @param y the divisor
* @return the result
*/
public static long divZero(final long x, final long y) {
return y == 0L ? 0L : x / y;
}
/**
* Implements long remainder but allows {@code x % 0} to be represented as 0. Useful when remainder of two longs
* is coerced to long.
* @param x the dividend
* @param y the divisor
* @return the remainder
*/
public static long remZero(final long x, final long y) {
return y == 0L ? 0L : x % y;
}
/**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*

View File

@ -84,7 +84,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
private transient WeakHashMap<Property, SoftReference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */
private transient WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory;
/** property listeners */
private transient PropertyListeners listeners;
@ -677,14 +677,14 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
/**
* Check prototype history for an existing property map with specified prototype.
*
* @param parentMap New prototype object.
* @param proto New prototype object.
*
* @return Existing {@link PropertyMap} or {@code null} if not found.
*/
private PropertyMap checkProtoHistory(final PropertyMap parentMap) {
private PropertyMap checkProtoHistory(final ScriptObject proto) {
final PropertyMap cachedMap;
if (protoHistory != null) {
final SoftReference<PropertyMap> weakMap = protoHistory.get(parentMap);
final SoftReference<PropertyMap> weakMap = protoHistory.get(proto);
cachedMap = (weakMap != null ? weakMap.get() : null);
} else {
cachedMap = null;
@ -700,15 +700,15 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
/**
* Add a map to the prototype history.
*
* @param parentMap Prototype to add (key.)
* @param newProto Prototype to add (key.)
* @param newMap {@link PropertyMap} associated with prototype.
*/
private void addToProtoHistory(final PropertyMap parentMap, final PropertyMap newMap) {
private void addToProtoHistory(final ScriptObject newProto, final PropertyMap newMap) {
if (protoHistory == null) {
protoHistory = new WeakHashMap<>();
}
protoHistory.put(parentMap, new SoftReference<>(newMap));
protoHistory.put(newProto, new SoftReference<>(newMap));
}
/**
@ -883,8 +883,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
*/
public PropertyMap changeProto(final ScriptObject newProto) {
final PropertyMap parentMap = newProto == null ? null : newProto.getMap();
final PropertyMap nextMap = checkProtoHistory(parentMap);
final PropertyMap nextMap = checkProtoHistory(newProto);
if (nextMap != null) {
return nextMap;
}
@ -894,7 +893,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
}
final PropertyMap newMap = new PropertyMap(this);
addToProtoHistory(parentMap, newMap);
addToProtoHistory(newProto, newMap);
return newMap;
}

View File

@ -475,6 +475,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
* neither an existing map or a persistent cached type info is available.
*/
@SuppressWarnings("unused")
private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
if(invalidatedProgramPoints != null) {
@ -727,7 +728,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
assert existingBest != null;
//we are calling a vararg method with real args
boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
boolean varArgWithRealArgs = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
//if the best one is an apply to call, it has to match the callsite exactly
//or we need to regenerate
@ -736,14 +737,16 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if (best != null) {
return best;
}
applyToCall = true;
varArgWithRealArgs = true;
}
if (applyToCall) {
if (varArgWithRealArgs) {
// special case: we had an apply to call, but we failed to make it fit.
// Try to generate a specialized one for this callsite. It may
// be another apply to call specialization, or it may not, but whatever
// it is, it is a specialization that is guaranteed to fit
final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work
existingBest = addCode(fnInit, callSiteType);
}
existingBest = addCode(fnInit, callSiteType);
}
return existingBest;

View File

@ -212,6 +212,7 @@ public final class ScriptEnvironment {
* @param out output print writer
* @param err error print writer
*/
@SuppressWarnings("unused")
public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
this.out = out;
this.err = err;

View File

@ -47,6 +47,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -922,7 +923,10 @@ public abstract class ScriptObject implements PropertyAccess {
if (property instanceof UserAccessorProperty) {
((UserAccessorProperty)property).setAccessors(this, getMap(), null);
}
Global.getConstants().delete(property.getKey());
final GlobalConstants globalConstants = getGlobalConstants();
if (globalConstants != null) {
globalConstants.delete(property.getKey());
}
return true;
}
}
@ -1983,9 +1987,12 @@ public abstract class ScriptObject implements PropertyAccess {
}
}
final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
if (cinv != null) {
return cinv;
final GlobalConstants globalConstants = getGlobalConstants();
if (globalConstants != null) {
final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
if (cinv != null) {
return cinv;
}
}
final Class<?> returnType = desc.getMethodType().returnType();
@ -2183,14 +2190,22 @@ public abstract class ScriptObject implements PropertyAccess {
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
if (cinv != null) {
return cinv;
final GlobalConstants globalConstants = getGlobalConstants();
if (globalConstants != null) {
final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
if (cinv != null) {
return cinv;
}
}
return inv;
}
private GlobalConstants getGlobalConstants() {
// Avoid hitting getContext() which might be costly for a non-Global unless needed.
return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
}
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (NashornCallSiteDescriptor.isStrict(desc)) {

View File

@ -98,6 +98,10 @@ public abstract class ArrayData {
@Override
public ArrayData ensure(final long safeIndex) {
if (safeIndex > 0L) {
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
return new SparseArrayData(this, safeIndex + 1);
}
//known to fit in int
return toRealArrayData((int)safeIndex).ensure(safeIndex);
}
return this;

View File

@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Handle arrays where the index is very large.
*/
class SparseArrayData extends ArrayData {
static final long MAX_DENSE_LENGTH = 16 * 512 * 1024;
static final int MAX_DENSE_LENGTH = 8 * 1024 * 1024;
/** Underlying array. */
private ArrayData underlying;

View File

@ -47,10 +47,9 @@ public final class RecompilationEvent extends RuntimeEvent<RewriteException> {
* @param level logging level
* @param rewriteException rewriteException wrapped by this RuntimEvent
* @param returnValue rewriteException return value - as we don't want to make
* {@link RewriteException#getReturnValueNonDestructive()} public, we pass it as
* {@code RewriteException.getReturnValueNonDestructive()} public, we pass it as
* an extra parameter, rather than querying the getter from another package.
*/
@SuppressWarnings("javadoc")
public RecompilationEvent(final Level level, final RewriteException rewriteException, final Object returnValue) {
super(level, rewriteException);
assert Context.getContext().getLogger(RecompilableScriptFunctionData.class).isEnabled() :

View File

@ -42,6 +42,8 @@ import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@ -106,6 +108,12 @@ public final class Bootstrap {
return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
}
});
factory.setAutoConversionStrategy(new MethodTypeConversionStrategy() {
@Override
public MethodHandle asType(final MethodHandle target, final MethodType newType) {
return unboxReturnType(target, newType);
}
});
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
if (relinkThreshold > -1) {
factory.setUnstableRelinkThreshold(relinkThreshold);
@ -420,4 +428,29 @@ public final class Bootstrap {
static GuardedInvocation asTypeSafeReturn(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
return inv == null ? null : inv.asTypeSafeReturn(linkerServices, desc.getMethodType());
}
/**
* Adapts the return type of the method handle with {@code explicitCastArguments} when it is an unboxing
* conversion. This will ensure that nulls are unwrapped to false or 0.
* @param target the target method handle
* @param newType the desired new type. Note that this method does not adapt the method handle completely to the
* new type, it only adapts the return type; this is allowed as per
* {@link DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}, which is what this method
* is used for.
* @return the method handle with adapted return type, if it required an unboxing conversion.
*/
private static MethodHandle unboxReturnType(final MethodHandle target, final MethodType newType) {
final MethodType targetType = target.type();
final Class<?> oldReturnType = targetType.returnType();
if (TypeUtilities.isWrapperType(oldReturnType)) {
final Class<?> newReturnType = newType.returnType();
if (newReturnType.isPrimitive()) {
// The contract of setAutoConversionStrategy is such that the difference between newType and targetType
// can only be JLS method invocation conversions.
assert TypeUtilities.isMethodInvocationConvertible(oldReturnType, newReturnType);
return MethodHandles.explicitCastArguments(target, targetType.changeReturnType(newReturnType));
}
}
return target;
}
}

View File

@ -25,8 +25,10 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.*;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETMEMBER;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETSLOT;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETMEMBER;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETSLOT;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
@ -114,12 +116,10 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
case "getMethod":
if (c > 2) {
return findGetMethod(desc);
} else {
// For indexed get, we want GuardedInvocation from beans linker and pass it.
// BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
return findGetIndexMethod(beanInv);
}
// For indexed get, we want GuardedInvocation from beans linker and pass it.
// BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
@ -166,9 +166,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
final String name = (String)key;
if (name.indexOf('(') != -1) {
return fallback.invokeExact(jsobj, key);
} else {
return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
}
return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
}
return null;
}

View File

@ -120,12 +120,10 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
case "getMethod":
if (c > 2) {
return findGetMethod(desc);
} else {
// For indexed get, we want get GuardedInvocation beans linker and pass it.
// JSObjectLinker.get uses this fallback getter for explicit signature method access.
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
return findGetIndexMethod(beanInv);
}
// For indexed get, we want get GuardedInvocation beans linker and pass it.
// JSObjectLinker.get uses this fallback getter for explicit signature method access.
return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
@ -192,9 +190,8 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
// get with method name and signature. delegate it to beans linker!
if (name.indexOf('(') != -1) {
return fallback.invokeExact(jsobj, key);
} else {
return ((JSObject)jsobj).getMember(name);
}
return ((JSObject)jsobj).getMember(name);
}
return null;
}

View File

@ -53,15 +53,34 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// Object type arguments of Java method calls, field set and array set.
private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class);
private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class);
private static final MethodHandle EXPORT_ARGUMENT;
private static final MethodHandle EXPORT_NATIVE_ARRAY;
private static final MethodHandle EXPORT_SCRIPT_OBJECT;
private static final MethodHandle IMPORT_RESULT;
private static final MethodHandle FILTER_CONSSTRING;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
EXPORT_NATIVE_ARRAY = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class);
FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
}
private final BeansLinker beansLinker = new BeansLinker();
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
if (linkRequest.getReceiver() instanceof ConsString) {
// In order to treat ConsString like a java.lang.String we need a link request with a string receiver.
final Object[] arguments = linkRequest.getArguments();
arguments[0] = "";
final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(linkRequest.getCallSiteDescriptor(), arguments);
final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices);
// If an invocation is found we add a filter that makes it work for both Strings and ConsStrings.
return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
}
return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
}
@ -113,6 +132,11 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return ScriptUtils.unwrap(arg);
}
@SuppressWarnings("unused")
private static Object consStringFilter(final Object arg) {
return arg instanceof ConsString ? arg.toString() : arg;
}
private static class NashornBeansLinkerServices implements LinkerServices {
private final LinkerServices linkerServices;

View File

@ -27,7 +27,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode.newAltNode;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
import java.util.HashSet;
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
@ -53,6 +52,7 @@ final class Analyser extends Parser {
super(env, chars, p, end);
}
@SuppressWarnings("unused")
protected final void compile() {
if (Config.DEBUG) {
Config.log.println(new String(chars, getBegin(), getEnd()));
@ -76,7 +76,9 @@ final class Analyser extends Parser {
root = setupTree(root, 0);
if (Config.DEBUG_PARSE_TREE) {
if (Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<TREE>");
if (Config.DEBUG_PARSE_TREE_RAW) {
Config.log.println("<TREE>");
}
root.verifyTree(new HashSet<Node>(), env.reg.warnings);
Config.log.println(root + "\n");
}
@ -94,7 +96,9 @@ final class Analyser extends Parser {
regex.clearOptimizeInfo();
if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root);
if (!Config.DONT_OPTIMIZE) {
setOptimizedInfoFromTree(root);
}
env.memNodes = null;
@ -110,7 +114,9 @@ final class Analyser extends Parser {
if (Config.DEBUG_COMPILE) {
Config.log.println("stack used: " + regex.stackNeeded);
if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n");
if (Config.USE_STRING_TEMPLATES) {
Config.log.print("templates: " + regex.templateNum + "\n");
}
Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
} // DEBUG_COMPILE
@ -136,7 +142,9 @@ final class Analyser extends Parser {
ConsAltNode can = (ConsAltNode)node;
do {
final int v = quantifiersMemoryInfo(can.car);
if (v > info) info = v;
if (v > info) {
info = v;
}
} while ((can = can.cdr) != null);
break;
@ -182,7 +190,9 @@ final class Analyser extends Parser {
switch (node.getType()) {
case NodeType.BREF:
final BackRefNode br = (BackRefNode)node;
if (br.isRecursion()) break;
if (br.isRecursion()) {
break;
}
if (br.backRef > env.numMem) {
throw new ValueException(ERR_INVALID_BACKREF);
@ -249,6 +259,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
min = getMinMatchLength(en.target);
break;
default:
break;
} // inner switch
break;
@ -276,7 +289,9 @@ final class Analyser extends Parser {
ConsAltNode an = (ConsAltNode)node;
do {
final int tmax = getMaxMatchLength(an.car);
if (max < tmax) max = tmax;
if (max < tmax) {
max = tmax;
}
} while ((an = an.cdr) != null);
break;
@ -304,7 +319,9 @@ final class Analyser extends Parser {
throw new ValueException(ERR_INVALID_BACKREF);
}
final int tmax = getMaxMatchLength(env.memNodes[br.backRef]);
if (max < tmax) max = tmax;
if (max < tmax) {
max = tmax;
}
break;
case NodeType.QTFR:
@ -338,6 +355,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
max = getMaxMatchLength(en.target);
break;
default:
break;
} // inner switch
break;
@ -355,8 +375,8 @@ final class Analyser extends Parser {
return getCharLengthTree(node, 0);
}
private int getCharLengthTree(final Node node, int level) {
level++;
private int getCharLengthTree(final Node node, final int levelp) {
final int level = levelp + 1;
int len = 0;
returnCode = 0;
@ -366,7 +386,9 @@ final class Analyser extends Parser {
ConsAltNode ln = (ConsAltNode)node;
do {
final int tlen = getCharLengthTree(ln.car, level);
if (returnCode == 0) len = MinMaxLen.distanceAdd(len, tlen);
if (returnCode == 0) {
len = MinMaxLen.distanceAdd(len, tlen);
}
} while (returnCode == 0 && (ln = ln.cdr) != null);
break;
@ -378,7 +400,9 @@ final class Analyser extends Parser {
while (returnCode == 0 && (an = an.cdr) != null) {
final int tlen2 = getCharLengthTree(an.car, level);
if (returnCode == 0) {
if (tlen != tlen2) varLen = true;
if (tlen != tlen2) {
varLen = true;
}
}
}
@ -404,7 +428,9 @@ final class Analyser extends Parser {
final QuantifierNode qn = (QuantifierNode)node;
if (qn.lower == qn.upper) {
tlen = getCharLengthTree(qn.target, level);
if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);
if (returnCode == 0) {
len = MinMaxLen.distanceMultiply(tlen, qn.lower);
}
} else {
returnCode = GET_CHAR_LEN_VARLEN;
}
@ -435,6 +461,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
len = getCharLengthTree(en.target, level);
break;
default:
break;
} // inner switch
break;
@ -448,7 +477,9 @@ final class Analyser extends Parser {
}
/* x is not included y ==> 1 : 0 */
private boolean isNotIncluded(Node x, Node y) {
private static boolean isNotIncluded(final Node xn, final Node yn) {
Node x = xn;
Node y = yn;
Node tmp;
// !retry:!
@ -492,10 +523,14 @@ final class Analyser extends Parser {
boolean v = xc.bs.at(i);
if ((v && !xc.isNot()) || (!v && xc.isNot())) {
v = yc.bs.at(i);
if ((v && !yc.isNot()) || (!v && yc.isNot())) return false;
if ((v && !yc.isNot()) || (!v && yc.isNot())) {
return false;
}
}
}
if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) return true;
if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) {
return true;
}
return false;
// break; not reached
@ -514,7 +549,9 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode xs = (StringNode)x;
if (xs.length() == 0) break;
if (xs.length() == 0) {
break;
}
switch (yType) {
@ -526,13 +563,16 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode ys = (StringNode)y;
int len = xs.length();
if (len > ys.length()) len = ys.length();
if (len > ys.length()) {
len = ys.length();
}
if (xs.isAmbig() || ys.isAmbig()) {
/* tiny version */
return false;
} else {
for (int i=0, p=ys.p, q=xs.p; i<len; i++, p++, q++) {
if (ys.chars[p] != xs.chars[q]) return true;
}
for (int i=0, pt=ys.p, q=xs.p; i<len; i++, pt++, q++) {
if (ys.chars[pt] != xs.chars[q]) {
return true;
}
}
break;
@ -542,6 +582,8 @@ final class Analyser extends Parser {
} // inner switch
break; // case NodeType.STR
default:
break;
} // switch
@ -561,7 +603,9 @@ final class Analyser extends Parser {
case NodeType.CTYPE:
case NodeType.CCLASS:
if (!exact) n = node;
if (!exact) {
n = node;
}
break;
case NodeType.LIST:
@ -570,7 +614,10 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode sn = (StringNode)node;
if (sn.end <= sn.p) break; // ???
if (sn.end <= sn.p)
{
break; // ???
}
if (exact && !sn.isRaw() && isIgnoreCase(regex.options)){
// nothing
@ -605,12 +652,17 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
n = getHeadValueNode(en.target, exact);
break;
default:
break;
} // inner switch
break;
case NodeType.ANCHOR:
final AnchorNode an = (AnchorNode)node;
if (an.type == AnchorType.PREC_READ) n = getHeadValueNode(an.target, exact);
if (an.type == AnchorType.PREC_READ) {
n = getHeadValueNode(an.target, exact);
}
break;
default:
@ -622,7 +674,9 @@ final class Analyser extends Parser {
// true: invalid
private boolean checkTypeTree(final Node node, final int typeMask, final int encloseMask, final int anchorMask) {
if ((node.getType2Bit() & typeMask) == 0) return true;
if ((node.getType2Bit() & typeMask) == 0) {
return true;
}
boolean invalid = false;
@ -641,15 +695,21 @@ final class Analyser extends Parser {
case NodeType.ENCLOSE:
final EncloseNode en = (EncloseNode)node;
if ((en.type & encloseMask) == 0) return true;
if ((en.type & encloseMask) == 0) {
return true;
}
invalid = checkTypeTree(en.target, typeMask, encloseMask, anchorMask);
break;
case NodeType.ANCHOR:
final AnchorNode an = (AnchorNode)node;
if ((an.type & anchorMask) == 0) return true;
if ((an.type & anchorMask) == 0) {
return true;
}
if (an.target != null) invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
if (an.target != null) {
invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
}
break;
default:
@ -664,7 +724,8 @@ final class Analyser extends Parser {
(?<=A|B) ==> (?<=A)|(?<=B)
(?<!A|B) ==> (?<!A)(?<!B)
*/
private Node divideLookBehindAlternatives(Node node) {
private Node divideLookBehindAlternatives(final Node nodep) {
Node node = nodep;
final AnchorNode an = (AnchorNode)node;
final int anchorType = an.type;
Node head = an.target;
@ -699,7 +760,7 @@ final class Analyser extends Parser {
private Node setupLookBehind(final Node node) {
final AnchorNode an = (AnchorNode)node;
final int len = getCharLengthTree(an.target);
switch(returnCode) {
switch (returnCode) {
case 0:
an.charLength = len;
break;
@ -708,14 +769,17 @@ final class Analyser extends Parser {
case GET_CHAR_LEN_TOP_ALT_VARLEN:
if (syntax.differentLengthAltLookBehind()) {
return divideLookBehindAlternatives(node);
} else {
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
default:
break;
}
return node;
}
private void nextSetup(Node node, final Node nextNode) {
private void nextSetup(final Node nodep, final Node nextNode) {
Node node = nodep;
// retry:
retry: while(true) {
@ -762,7 +826,7 @@ final class Analyser extends Parser {
}
private void updateStringNodeCaseFoldMultiByte(final StringNode sn) {
final char[] chars = sn.chars;
final char[] ch = sn.chars;
final int end = sn.end;
value = sn.p;
int sp = 0;
@ -770,15 +834,15 @@ final class Analyser extends Parser {
while (value < end) {
final int ovalue = value;
buf = EncodingHelper.toLowerCase(chars[value++]);
buf = EncodingHelper.toLowerCase(ch[value++]);
if (chars[ovalue] != buf) {
if (ch[ovalue] != buf) {
char[] sbuf = new char[sn.length() << 1];
System.arraycopy(chars, sn.p, sbuf, 0, ovalue - sn.p);
System.arraycopy(ch, sn.p, sbuf, 0, ovalue - sn.p);
value = ovalue;
while (value < end) {
buf = EncodingHelper.toLowerCase(chars[value++]);
buf = EncodingHelper.toLowerCase(ch[value++]);
if (sp >= sbuf.length) {
final char[]tmp = new char[sbuf.length << 1];
System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
@ -798,8 +862,8 @@ final class Analyser extends Parser {
updateStringNodeCaseFoldMultiByte(sn);
}
private Node expandCaseFoldMakeRemString(final char[] chars, final int p, final int end) {
final StringNode node = new StringNode(chars, p, end);
private Node expandCaseFoldMakeRemString(final char[] ch, final int pp, final int end) {
final StringNode node = new StringNode(ch, pp, end);
updateStringNodeCaseFold(node);
node.setAmbig();
@ -807,7 +871,7 @@ final class Analyser extends Parser {
return node;
}
private boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
private static boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
final char[] chars, final int p, final int slen, final int end, final ObjPtr<Node> node) {
ConsAltNode altNode;
@ -833,59 +897,68 @@ final class Analyser extends Parser {
private Node expandCaseFoldString(final Node node) {
final StringNode sn = (StringNode)node;
if (sn.isAmbig() || sn.length() <= 0) return node;
if (sn.isAmbig() || sn.length() <= 0) {
return node;
}
final char[] chars = sn.chars;
int p = sn.p;
final char[] chars1 = sn.chars;
int pt = sn.p;
final int end = sn.end;
int altNum = 1;
ConsAltNode topRoot = null, root = null;
ConsAltNode topRoot = null, r = null;
@SuppressWarnings("unused")
final ObjPtr<Node> prevNode = new ObjPtr<Node>();
StringNode stringNode = null;
while (p < end) {
final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars[p]);
while (pt < end) {
final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars1[pt]);
if (items.length == 0) {
if (stringNode == null) {
if (root == null && prevNode.p != null) {
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
if (r == null && prevNode.p != null) {
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
prevNode.p = stringNode = new StringNode(); // onig_node_new_str(NULL, NULL);
if (root != null) ConsAltNode.listAdd(root, stringNode);
if (r != null) {
ConsAltNode.listAdd(r, stringNode);
}
}
stringNode.cat(chars, p, p + 1);
stringNode.cat(chars1, pt, pt + 1);
} else {
altNum *= (items.length + 1);
if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) break;
if (root == null && prevNode.p != null) {
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) {
break;
}
expandCaseFoldStringAlt(items.length, items, chars, p, 1, end, prevNode);
if (root != null) ConsAltNode.listAdd(root, prevNode.p);
if (r == null && prevNode.p != null) {
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
expandCaseFoldStringAlt(items.length, items, chars1, pt, 1, end, prevNode);
if (r != null) {
ConsAltNode.listAdd(r, prevNode.p);
}
stringNode = null;
}
p++;
pt++;
}
if (p < end) {
final Node srem = expandCaseFoldMakeRemString(chars, p, end);
if (pt < end) {
final Node srem = expandCaseFoldMakeRemString(chars1, pt, end);
if (prevNode.p != null && root == null) {
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
if (prevNode.p != null && r == null) {
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
if (root == null) {
if (r == null) {
prevNode.p = srem;
} else {
ConsAltNode.listAdd(root, srem);
ConsAltNode.listAdd(r, srem);
}
}
/* ending */
@ -909,7 +982,10 @@ final class Analyser extends Parser {
5. find invalid patterns in look-behind.
6. expand repeated string.
*/
protected final Node setupTree(Node node, int state) {
protected final Node setupTree(final Node nodep, final int statep) {
Node node = nodep;
int state = statep;
restart: while (true) {
switch (node.getType()) {
case NodeType.LIST:
@ -958,7 +1034,9 @@ final class Analyser extends Parser {
final QuantifierNode qn = (QuantifierNode)node;
Node target = qn.target;
if ((state & IN_REPEAT) != 0) qn.setInRepeat();
if ((state & IN_REPEAT) != 0) {
qn.setInRepeat();
}
if (isRepeatInfinite(qn.upper) || qn.lower >= 1) {
final int d = getMinMatchLength(target);
@ -966,14 +1044,18 @@ final class Analyser extends Parser {
qn.targetEmptyInfo = TargetInfo.IS_EMPTY;
if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
final int info = quantifiersMemoryInfo(target);
if (info > 0) qn.targetEmptyInfo = info;
if (info > 0) {
qn.targetEmptyInfo = info;
}
} // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
// strange stuff here (turned off)
}
}
state |= IN_REPEAT;
if (qn.lower != qn.upper) state |= IN_VAR_REPEAT;
if (qn.lower != qn.upper) {
state |= IN_VAR_REPEAT;
}
target = setupTree(target, state);
@ -1035,11 +1117,16 @@ final class Analyser extends Parser {
final QuantifierNode tqn = (QuantifierNode)en.target;
if (isRepeatInfinite(tqn.upper) && tqn.lower <= 1 && tqn.greedy) {
/* (?>a*), a*+ etc... */
if (tqn.target.isSimple()) en.setStopBtSimpleRepeat();
if (tqn.target.isSimple()) {
en.setStopBtSimpleRepeat();
}
}
}
break;
default:
break;
} // inner switch
break;
@ -1059,7 +1146,9 @@ final class Analyser extends Parser {
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
node = setupLookBehind(node);
if (node.getType() != NodeType.ANCHOR) continue restart;
if (node.getType() != NodeType.ANCHOR) {
continue restart;
}
setupTree(((AnchorNode)node).target, state);
break;
@ -1068,12 +1157,19 @@ final class Analyser extends Parser {
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
node = setupLookBehind(node);
if (node.getType() != NodeType.ANCHOR) continue restart;
if (node.getType() != NodeType.ANCHOR) {
continue restart;
}
setupTree(((AnchorNode)node).target, (state | IN_NOT));
break;
default:
break;
} // inner switch
break;
default:
break;
} // switch
return node;
} // restart: while
@ -1191,7 +1287,9 @@ final class Analyser extends Parser {
opt.expr.copy(nopt.exm);
}
opt.expr.reachEnd = false;
if (nopt.map.value > 0) opt.map.copy(nopt.map);
if (nopt.map.value > 0) {
opt.map.copy(nopt.map);
}
break;
case AnchorType.PREC_READ_NOT:
@ -1199,6 +1297,9 @@ final class Analyser extends Parser {
case AnchorType.LOOK_BEHIND_NOT:
break;
default:
break;
} // inner switch
break;
}
@ -1282,8 +1383,12 @@ final class Analyser extends Parser {
if (++en.optCount > MAX_NODE_OPT_INFO_REF_COUNT) {
int min = 0;
int max = MinMaxLen.INFINITE_DISTANCE;
if (en.isMinFixed()) min = en.minLength;
if (en.isMaxFixed()) max = en.maxLength;
if (en.isMinFixed()) {
min = en.minLength;
}
if (en.isMaxFixed()) {
max = en.maxLength;
}
opt.length.set(min, max);
} else { // USE_SUBEXP_CALL
optimizeNodeLeft(en.target, opt, oenv);
@ -1298,6 +1403,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
optimizeNodeLeft(en.target, opt, oenv);
break;
default:
break;
} // inner switch
break;
}
@ -1307,6 +1415,7 @@ final class Analyser extends Parser {
} // switch
}
@SuppressWarnings("unused")
protected final void setOptimizedInfoFromTree(final Node node) {
final NodeOptInfo opt = new NodeOptInfo();
final OptEnvironment oenv = new OptEnvironment();
@ -1347,7 +1456,9 @@ final class Analyser extends Parser {
regex.setSubAnchor(opt.map.anchor);
} else {
regex.subAnchor |= opt.anchor.leftAnchor & AnchorType.BEGIN_LINE;
if (opt.length.max == 0) regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
if (opt.length.max == 0) {
regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
}
}
if (Config.DEBUG_COMPILE || Config.DEBUG_MATCH) {

View File

@ -24,7 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
final class ApplyCaseFold {
// i_apply_case_fold
public void apply(final int from, final int to, final Object o) {
public static void apply(final int from, final int to, final Object o) {
final ApplyCaseFoldArg arg = (ApplyCaseFoldArg)o;
final ScanEnvironment env = arg.env;
@ -45,7 +45,9 @@ final class ApplyCaseFold {
} else {
if (inCC) {
if (to >= BitSet.SINGLE_BYTE_SIZE) {
if (cc.isNot()) cc.clearNotFlag();
if (cc.isNot()) {
cc.clearNotFlag();
}
cc.addCodeRange(env, to, to);
} else {
if (cc.isNot()) {

View File

@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
@SuppressWarnings("javadoc")
public final class ApplyCaseFoldArg {
final ScanEnvironment env;
final CClassNode cc;

View File

@ -24,7 +24,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isDynamic;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
@ -98,15 +97,15 @@ final class ArrayCompiler extends Compiler {
} while ((aln = aln.cdr) != null);
}
private boolean isNeedStrLenOpExact(final int op) {
private static boolean isNeedStrLenOpExact(final int op) {
return op == OPCode.EXACTN || op == OPCode.EXACTN_IC;
}
private boolean opTemplated(final int op) {
private static boolean opTemplated(final int op) {
return isNeedStrLenOpExact(op);
}
private int selectStrOpcode(final int strLength, final boolean ignoreCase) {
private static int selectStrOpcode(final int strLength, final boolean ignoreCase) {
int op;
if (ignoreCase) {
@ -139,7 +138,7 @@ final class ArrayCompiler extends Compiler {
compileTree(node);
if (emptyInfo != 0) {
switch(emptyInfo) {
switch (emptyInfo) {
case TargetInfo.IS_EMPTY:
addOpcode(OPCode.NULL_CHECK_END);
break;
@ -149,13 +148,15 @@ final class ArrayCompiler extends Compiler {
case TargetInfo.IS_EMPTY_REC:
addOpcode(OPCode.NULL_CHECK_END_MEMST_PUSH);
break;
default:
break;
} // switch
addMemNum(savedNumNullCheck); /* NULL CHECK ID */
}
}
private int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
private static int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
final int op = selectStrOpcode(strLength, ignoreCase);
int len = OPSize.OPCODE;
@ -163,7 +164,9 @@ final class ArrayCompiler extends Compiler {
// string length, template index, template string pointer
len += OPSize.LENGTH + OPSize.INDEX + OPSize.INDEX;
} else {
if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
if (isNeedStrLenOpExact(op)) {
len += OPSize.LENGTH;
}
len += strLength;
}
return len;
@ -187,9 +190,11 @@ final class ArrayCompiler extends Compiler {
}
}
private int compileLengthStringNode(final Node node) {
private static int compileLengthStringNode(final Node node) {
final StringNode sn = (StringNode)node;
if (sn.length() <= 0) return 0;
if (sn.length() <= 0) {
return 0;
}
final boolean ambig = sn.isAmbig();
int p, prev;
@ -210,8 +215,10 @@ final class ArrayCompiler extends Compiler {
return rlen;
}
private int compileLengthStringRawNode(final StringNode sn) {
if (sn.length() <= 0) return 0;
private static int compileLengthStringRawNode(final StringNode sn) {
if (sn.length() <= 0) {
return 0;
}
return addCompileStringlength(sn.chars, sn.p, sn.length(), false);
}
@ -220,8 +227,10 @@ final class ArrayCompiler extends Compiler {
addInts(mbuf.p, mbuf.used);
}
private int compileLengthCClassNode(final CClassNode cc) {
if (cc.isShare()) return OPSize.OPCODE + OPSize.POINTER;
private static int compileLengthCClassNode(final CClassNode cc) {
if (cc.isShare()) {
return OPSize.OPCODE + OPSize.POINTER;
}
int len;
if (cc.mbuf == null) {
@ -360,9 +369,8 @@ final class ArrayCompiler extends Compiler {
if (qn.greedy && infinite) {
if (qn.nextHeadExact != null) {
return OPSize.ANYCHAR_STAR_PEEK_NEXT + tlen * qn.lower;
} else {
return OPSize.ANYCHAR_STAR + tlen * qn.lower;
}
return OPSize.ANYCHAR_STAR + tlen * qn.lower;
}
}
@ -425,14 +433,13 @@ final class ArrayCompiler extends Compiler {
final StringNode sn = (StringNode)qn.nextHeadExact;
addChars(sn.chars, sn.p, 1);
return;
} else {
if (isMultiline(regex.options)) {
addOpcode(OPCode.ANYCHAR_ML_STAR);
} else {
addOpcode(OPCode.ANYCHAR_STAR);
}
return;
}
if (isMultiline(regex.options)) {
addOpcode(OPCode.ANYCHAR_ML_STAR);
} else {
addOpcode(OPCode.ANYCHAR_STAR);
}
return;
}
int modTLen;
@ -510,9 +517,8 @@ final class ArrayCompiler extends Compiler {
if (isDynamic(prev ^ node.option)) {
return OPSize.SET_OPTION_PUSH + OPSize.SET_OPTION + OPSize.FAIL + tlen + OPSize.SET_OPTION;
} else {
return tlen;
}
return tlen;
}
@Override
@ -675,13 +681,15 @@ final class ArrayCompiler extends Compiler {
break;
case AnchorType.WORD_BEGIN:
if (Config.USE_WORD_BEGIN_END)
if (Config.USE_WORD_BEGIN_END) {
addOpcode(OPCode.WORD_BEGIN);
}
break;
case AnchorType.WORD_END:
if (Config.USE_WORD_BEGIN_END)
if (Config.USE_WORD_BEGIN_END) {
addOpcode(OPCode.WORD_END);
}
break;
case AnchorType.PREC_READ:
@ -701,7 +709,9 @@ final class ArrayCompiler extends Compiler {
addOpcode(OPCode.LOOK_BEHIND);
if (node.charLength < 0) {
n = analyser.getCharLengthTree(node.target);
if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
if (analyser.returnCode != 0) {
newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
} else {
n = node.charLength;
}
@ -714,7 +724,9 @@ final class ArrayCompiler extends Compiler {
addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
if (node.charLength < 0) {
n = analyser.getCharLengthTree(node.target);
if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
if (analyser.returnCode != 0) {
newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
} else {
n = node.charLength;
}
@ -796,7 +808,9 @@ final class ArrayCompiler extends Compiler {
private void ensure(final int size) {
if (size >= code.length) {
int length = code.length << 1;
while (length <= size) length <<= 1;
while (length <= size) {
length <<= 1;
}
final int[]tmp = new int[length];
System.arraycopy(code, 0, tmp, 0, code.length);
code = tmp;
@ -829,11 +843,14 @@ final class ArrayCompiler extends Compiler {
regex.operands[regex.operandLength++] = o;
}
private void addChars(final char[] chars, int p ,final int length) {
private void addChars(final char[] chars, final int pp ,final int length) {
ensure(codeLength + length);
int p = pp;
final int end = p + length;
while (p < end) code[codeLength++] = chars[p++];
while (p < end) {
code[codeLength++] = chars[p++];
}
}
private void addInts(final int[]ints, final int length) {
@ -876,6 +893,9 @@ final class ArrayCompiler extends Compiler {
case OPCode.CALL:
case OPCode.RETURN: // it will appear only with CALL though
regex.stackNeeded = true;
break;
default:
break;
}
}

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public final class BitSet {
static final int BITS_PER_BYTE = 8;
public static final int SINGLE_BYTE_SIZE = (1 << BITS_PER_BYTE);
@ -34,7 +35,9 @@ public final class BitSet {
final StringBuilder buffer = new StringBuilder();
buffer.append("BitSet");
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) buffer.append("\n ");
if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) {
buffer.append("\n ");
}
buffer.append(at(i) ? "1" : "0");
}
return buffer.toString();
@ -53,44 +56,62 @@ public final class BitSet {
}
public void clear() {
for (int i=0; i<BITSET_SIZE; i++) bits[i]=0;
for (int i=0; i<BITSET_SIZE; i++) {
bits[i]=0;
}
}
public boolean isEmpty() {
for (int i=0; i<BITSET_SIZE; i++) {
if (bits[i] != 0) return false;
if (bits[i] != 0) {
return false;
}
}
return true;
}
public void setRange(final int from, final int to) {
for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) set(i);
for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) {
set(i);
}
}
public void invert() {
for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~bits[i];
for (int i=0; i<BITSET_SIZE; i++) {
bits[i] = ~bits[i];
}
}
public void invertTo(final BitSet to) {
for (int i=0; i<BITSET_SIZE; i++) to.bits[i] = ~bits[i];
for (int i=0; i<BITSET_SIZE; i++) {
to.bits[i] = ~bits[i];
}
}
public void and(final BitSet other) {
for (int i=0; i<BITSET_SIZE; i++) bits[i] &= other.bits[i];
for (int i=0; i<BITSET_SIZE; i++) {
bits[i] &= other.bits[i];
}
}
public void or(final BitSet other) {
for (int i=0; i<BITSET_SIZE; i++) bits[i] |= other.bits[i];
for (int i=0; i<BITSET_SIZE; i++) {
bits[i] |= other.bits[i];
}
}
public void copy(final BitSet other) {
for (int i=0; i<BITSET_SIZE; i++) bits[i] = other.bits[i];
for (int i=0; i<BITSET_SIZE; i++) {
bits[i] = other.bits[i];
}
}
public int numOn() {
int num = 0;
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
if (at(i)) num++;
if (at(i)) {
num++;
}
}
return num;
}
@ -99,9 +120,12 @@ public final class BitSet {
return 1 << (pos % SINGLE_BYTE_SIZE);
}
private static int log2(int n){
private static int log2(final int np) {
int log = 0;
while ((n >>>= 1) != 0) log++;
int n = np;
while ((n >>>= 1) != 0) {
log++;
}
return log;
}

View File

@ -34,7 +34,8 @@ final class BitStatus {
return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
}
public static int bsOnAt(int stats, final int n) {
public static int bsOnAt(final int statsp, final int n) {
int stats = statsp;
if (n < BIT_STATUS_BITS_NUM) {
stats |= (1 << n);
} else {
@ -43,12 +44,7 @@ final class BitStatus {
return stats;
}
public static int bsOnOff(int v, final int f, final boolean negative) {
if (negative) {
v &= ~f;
} else {
v |= f;
}
return v;
public static int bsOnOff(final int v, final int f, final boolean negative) {
return negative ? (v & ~f) : (v | f);
}
}

View File

@ -27,10 +27,8 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindNotEmpty;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotBol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotEol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isPosixRegion;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPSize;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
@ -52,8 +50,8 @@ class ByteCodeMachine extends StackMachine {
this.code = regex.code;
}
private boolean stringCmpIC(final int caseFlodFlag, int s1, final IntHolder ps2, final int mbLen, final int textEnd) {
private boolean stringCmpIC(final int caseFlodFlag, final int s1p, final IntHolder ps2, final int mbLen, final int textEnd) {
int s1 = s1p;
int s2 = ps2.value;
final int end1 = s1 + mbLen;
@ -83,12 +81,16 @@ class ByteCodeMachine extends StackMachine {
Config.log.printf("%4d", (s - str)).print("> \"");
int q, i;
for (i=0, q=s; i<7 && q<end && s>=0; i++) {
if (q < end) Config.log.print(new String(new char[]{chars[q++]}));
if (q < end) {
Config.log.print(new String(new char[]{chars[q++]}));
}
}
final String string = q < end ? "...\"" : "\"";
q += string.length();
Config.log.print(string);
for (i=0; i<20-(q-s);i++) {
Config.log.print(" ");
}
final String str = q < end ? "...\"" : "\"";
q += str.length();
Config.log.print(str);
for (i=0; i<20-(q-s);i++) Config.log.print(" ");
final StringBuilder sb = new StringBuilder();
new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
Config.log.println(sb.toString());
@ -96,28 +98,34 @@ class ByteCodeMachine extends StackMachine {
}
@Override
protected final int matchAt(final int range, final int sstart, final int sprev) {
this.range = range;
this.sstart = sstart;
this.sprev = sprev;
protected final int matchAt(final int r, final int ss, final int sp) {
this.range = r;
this.sstart = ss;
this.sprev = sp;
stk = 0;
ip = 0;
if (Config.DEBUG_MATCH) debugMatchBegin();
if (Config.DEBUG_MATCH) {
debugMatchBegin();
}
init();
bestLen = -1;
s = sstart;
s = ss;
final int[]code = this.code;
final int[] c = this.code;
while (true) {
if (Config.DEBUG_MATCH) debugMatchLoop();
if (Config.DEBUG_MATCH) {
debugMatchLoop();
}
sbegin = s;
switch (code[ip++]) {
case OPCode.END: if (opEnd()) return finish(); break;
switch (c[ip++]) {
case OPCode.END: if (opEnd()) {
return finish();
} break;
case OPCode.EXACT1: opExact1(); break;
case OPCode.EXACT2: opExact2(); continue;
case OPCode.EXACT3: opExact3(); continue;
@ -358,10 +366,14 @@ class ByteCodeMachine extends StackMachine {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
while (tlen-- > 0) if (bs[ps++] != chars[s++]) {opFail(); return;}
while (tlen-- > 0) {
if (bs[ps++] != chars[s++]) {opFail(); return;}
}
} else {
while (tlen-- > 0) if (code[ip++] != chars[s++]) {opFail(); return;}
while (tlen-- > 0) {
if (code[ip++] != chars[s++]) {opFail(); return;}
}
}
sprev = s - 1;
}
@ -380,10 +392,14 @@ class ByteCodeMachine extends StackMachine {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
while (tlen-- > 0) if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
while (tlen-- > 0) {
if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
}
} else {
while (tlen-- > 0) if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
while (tlen-- > 0) {
if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
}
}
sprev = s - 1;
}
@ -402,11 +418,15 @@ class ByteCodeMachine extends StackMachine {
private boolean isInClassMB() {
final int tlen = code[ip++];
if (s >= range) return false;
if (s >= range) {
return false;
}
final int ss = s;
s++;
final int c = chars[ss];
if (!EncodingHelper.isInCodeRange(code, ip, c)) return false;
if (!EncodingHelper.isInCodeRange(code, ip, c)) {
return false;
}
ip += tlen;
return true;
}
@ -444,7 +464,9 @@ class ByteCodeMachine extends StackMachine {
final int tlen = code[ip++];
if (!(s + 1 <= range)) {
if (s >= range) return false;
if (s >= range) {
return false;
}
s = end;
ip += tlen;
return true;
@ -454,7 +476,9 @@ class ByteCodeMachine extends StackMachine {
s++;
final int c = chars[ss];
if (EncodingHelper.isInCodeRange(code, ip, c)) return false;
if (EncodingHelper.isInCodeRange(code, ip, c)) {
return false;
}
ip += tlen;
return true;
}
@ -511,10 +535,10 @@ class ByteCodeMachine extends StackMachine {
}
private void opAnyCharStar() {
final char[] chars = this.chars;
final char[] ch = this.chars;
while (s < range) {
pushAlt(ip, s, sprev);
if (isNewLine(chars, s, end)) {opFail(); return;}
if (isNewLine(ch, s, end)) {opFail(); return;}
sprev = s;
s++;
}
@ -532,11 +556,13 @@ class ByteCodeMachine extends StackMachine {
private void opAnyCharStarPeekNext() {
final char c = (char)code[ip];
final char[] chars = this.chars;
final char[] ch = this.chars;
while (s < range) {
final char b = chars[s];
if (c == b) pushAlt(ip + 1, s, sprev);
final char b = ch[s];
if (c == b) {
pushAlt(ip + 1, s, sprev);
}
if (isNewLine(b)) {opFail(); return;}
sprev = s;
s++;
@ -547,10 +573,12 @@ class ByteCodeMachine extends StackMachine {
private void opAnyCharMLStarPeekNext() {
final char c = (char)code[ip];
final char[] chars = this.chars;
final char[] ch = this.chars;
while (s < range) {
if (c == chars[s]) pushAlt(ip + 1, s, sprev);
if (c == ch[s]) {
pushAlt(ip + 1, s, sprev);
}
sprev = s;
s++;
}
@ -592,29 +620,39 @@ class ByteCodeMachine extends StackMachine {
private void opWordBegin() {
if (s < range && EncodingHelper.isWord(chars[s])) {
if (s == str || !EncodingHelper.isWord(chars[sprev])) return;
if (s == str || !EncodingHelper.isWord(chars[sprev])) {
return;
}
}
opFail();
}
private void opWordEnd() {
if (s != str && EncodingHelper.isWord(chars[sprev])) {
if (s == end || !EncodingHelper.isWord(chars[s])) return;
if (s == end || !EncodingHelper.isWord(chars[s])) {
return;
}
}
opFail();
}
private void opBeginBuf() {
if (s != str) opFail();
if (s != str) {
opFail();
}
}
private void opEndBuf() {
if (s != end) opFail();
if (s != end) {
opFail();
}
}
private void opBeginLine() {
if (s == str) {
if (isNotBol(msaOptions)) opFail();
if (isNotBol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, sprev, end) && s != end) {
return;
@ -626,13 +664,16 @@ class ByteCodeMachine extends StackMachine {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
if (isNotEol(msaOptions)) opFail();
if (isNotEol(msaOptions)) {
opFail();
}
}
return;
} else {
if (isNotEol(msaOptions)) opFail();
return;
}
if (isNotEol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, s, end)) {
return;
}
@ -643,13 +684,16 @@ class ByteCodeMachine extends StackMachine {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
if (isNotEol(msaOptions)) opFail();
if (isNotEol(msaOptions)) {
opFail();
}
}
return;
} else {
if (isNotEol(msaOptions)) opFail();
return;
}
if (isNotEol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, s, end) && s + 1 == end) {
return;
}
@ -657,7 +701,9 @@ class ByteCodeMachine extends StackMachine {
}
private void opBeginPosition() {
if (s != msaStart) opFail();
if (s != msaStart) {
opFail();
}
}
private void opMemoryStartPush() {
@ -726,11 +772,15 @@ class ByteCodeMachine extends StackMachine {
sprev = s;
// STRING_CMP
while(n-- > 0) if (chars[pstart++] != chars[s++]) {opFail(); return;}
while(n-- > 0) {
if (chars[pstart++] != chars[s++]) {opFail(); return;}
}
// beyond string check
if (sprev < range) {
while (sprev + 1 < s) sprev++;
while (sprev + 1 < s) {
sprev++;
}
}
}
@ -764,7 +814,9 @@ class ByteCodeMachine extends StackMachine {
s = value;
// if (sprev < chars.length)
while (sprev + 1 < s) sprev++;
while (sprev + 1 < s) {
sprev++;
}
}
private void opBackRefMulti() {
@ -773,7 +825,9 @@ class ByteCodeMachine extends StackMachine {
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
if (backrefInvalid(mem)) continue;
if (backrefInvalid(mem)) {
continue;
}
int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
@ -785,14 +839,18 @@ class ByteCodeMachine extends StackMachine {
int swork = s;
while (n-- > 0) {
if (chars[pstart++] != chars[swork++]) continue loop;
if (chars[pstart++] != chars[swork++]) {
continue loop;
}
}
s = swork;
// beyond string check
if (sprev < range) {
while (sprev + 1 < s) sprev++;
while (sprev + 1 < s) {
sprev++;
}
}
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
@ -807,7 +865,9 @@ class ByteCodeMachine extends StackMachine {
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
if (backrefInvalid(mem)) continue;
if (backrefInvalid(mem)) {
continue;
}
final int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
@ -818,11 +878,16 @@ class ByteCodeMachine extends StackMachine {
sprev = s;
value = s;
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end)) continue loop; // STRING_CMP_VALUE_IC
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end))
{
continue loop; // STRING_CMP_VALUE_IC
}
s = value;
// if (sprev < chars.length)
while (sprev + 1 < s) sprev++;
while (sprev + 1 < s) {
sprev++;
}
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
break; /* success */
@ -830,10 +895,12 @@ class ByteCodeMachine extends StackMachine {
if (i == tlen) {opFail(); return;}
}
private boolean memIsInMemp(final int mem, final int num, int memp) {
for (int i=0; i<num; i++) {
private boolean memIsInMemp(final int mem, final int num, final int mempp) {
for (int i=0, memp = mempp; i<num; i++) {
final int m = code[memp++];
if (mem == m) return true;
if (mem == m) {
return true;
}
}
return false;
}
@ -857,7 +924,9 @@ class ByteCodeMachine extends StackMachine {
if (memIsInMemp(e.getMemNum(), memNum, memp)) {
final int pstart = e.getMemPStr();
if (pend != -1) {
if (pend - pstart > end - s) return false; /* or goto next_mem; */
if (pend - pstart > end - s) {
return false; /* or goto next_mem; */
}
int p = pstart;
value = s;
@ -867,7 +936,9 @@ class ByteCodeMachine extends StackMachine {
}
} else {
while (p < pend) {
if (chars[p++] != chars[value++]) return false; /* or goto next_mem; */
if (chars[p++] != chars[value++]) {
return false; /* or goto next_mem; */
}
}
}
s = value;
@ -893,24 +964,15 @@ class ByteCodeMachine extends StackMachine {
sprev = s;
if (backrefMatchAtNestedLevel(ic != 0, regex.caseFoldFlag, level, tlen, ip)) { // (s) and (end) implicit
while (sprev + 1 < s) sprev++;
while (sprev + 1 < s) {
sprev++;
}
ip += tlen; // * SIZE_MEMNUM
} else {
{opFail(); return;}
}
}
/* no need: IS_DYNAMIC_OPTION() == 0 */
private void opSetOptionPush() {
// option = code[ip++]; // final for now
pushAlt(ip, s, sprev);
ip += OPSize.SET_OPTION + OPSize.FAIL;
}
private void opSetOption() {
// option = code[ip++]; // final for now
}
private void opNullCheckStart() {
final int mem = code[ip++];
pushNullCheckStart(mem, s);
@ -1142,13 +1204,6 @@ class ByteCodeMachine extends StackMachine {
sprev = EncodingHelper.prevCharHead(str, s);
}
private void opLookBehindSb() {
final int tlen = code[ip++];
s -= tlen;
if (s < str) {opFail(); return;}
sprev = s == str ? -1 : s - 1;
}
private void opPushLookBehindNot() {
final int addr = code[ip++];
final int tlen = code[ip++];

View File

@ -236,16 +236,17 @@ class ByteCodePrinter {
sb.append(new String(code, s, len));
}
private void pLenStringFromTemplate(final StringBuilder sb, final int len, final char[] tm, final int idx) {
private static void pLenStringFromTemplate(final StringBuilder sb, final int len, final char[] tm, final int idx) {
sb.append(":T:").append(len).append(":");
sb.append(tm, idx, len);
}
public int compiledByteCodeToString(final StringBuilder sb, int bp) {
public int compiledByteCodeToString(final StringBuilder sb, final int bptr) {
int len, n, mem, addr, scn, cod;
BitSet bs;
CClassNode cc;
int tm, idx;
int bp = bptr;
sb.append("[").append(OpCodeNames[code[bp]]);
final int argType = OpCodeArgTypes[code[bp]];
@ -253,6 +254,7 @@ class ByteCodePrinter {
if (argType != Arguments.SPECIAL) {
bp++;
switch (argType) {
default:
case Arguments.NON:
break;
@ -410,7 +412,9 @@ class ByteCodePrinter {
for (int i=0; i<len; i++) {
mem = code[bp];
bp += OPSize.MEMNUM;
if (i > 0) sb.append(", ");
if (i > 0) {
sb.append(", ");
}
sb.append(mem);
}
break;
@ -428,7 +432,9 @@ class ByteCodePrinter {
for (int i=0; i<len; i++) {
mem = code[bp];
bp += OPSize.MEMNUM;
if (i > 0) sb.append(", ");
if (i > 0) {
sb.append(", ");
}
sb.append(mem);
}
break;
@ -501,7 +507,9 @@ class ByteCodePrinter {
while (bp < end) {
ncode++;
if (bp > 0) sb.append(ncode % 5 == 0 ? "\n" : " ");
if (bp > 0) {
sb.append(ncode % 5 == 0 ? "\n" : " ");
}
bp = compiledByteCodeToString(sb, bp);
}

View File

@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
@SuppressWarnings("javadoc")
public final class CodeRangeBuffer implements Cloneable {
private static final int INIT_MULTI_BYTE_RANGE_SIZE = 5;
private static final int ALL_MULTI_BYTE_RANGE = 0x7fffffff;
@ -68,7 +69,9 @@ public final class CodeRangeBuffer implements Cloneable {
for (int i=0; i<p[0]; i++) {
buf.append("[").append(rangeNumToString(p[i * 2 + 1])).append("..").append(rangeNumToString(p[i * 2 + 2])).append("]");
if (i > 0 && i % 6 == 0) buf.append("\n ");
if (i > 0 && i % 6 == 0) {
buf.append("\n ");
}
}
return buf.toString();
@ -97,9 +100,13 @@ public final class CodeRangeBuffer implements Cloneable {
}
private void moveRight(final int from, final int to, final int n) {
if (to + n > p.length) expand(to + n);
if (to + n > p.length) {
expand(to + n);
}
System.arraycopy(p, from, p, to, n);
if (to + n > used) used = to + n;
if (to + n > used) {
used = to + n;
}
}
protected void moveLeft(final int from, final int to, final int n) {
@ -113,9 +120,13 @@ public final class CodeRangeBuffer implements Cloneable {
public void writeCodePoint(final int pos, final int b) {
final int u = pos + 1;
if (p.length < u) expand(u);
if (p.length < u) {
expand(u);
}
p[pos] = b;
if (used < u) used = u;
if (used < u) {
used = u;
}
}
@Override
@ -125,14 +136,19 @@ public final class CodeRangeBuffer implements Cloneable {
// ugly part: these methods should be made OO
// add_code_range_to_buf
public static CodeRangeBuffer addCodeRangeToBuff(CodeRangeBuffer pbuf, int from, int to) {
public static CodeRangeBuffer addCodeRangeToBuff(final CodeRangeBuffer pbufp, final int fromp, final int top) {
int from = fromp, to = top;
CodeRangeBuffer pbuf = pbufp;
if (from > to) {
final int n = from;
from = to;
to = n;
}
if (pbuf == null) pbuf = new CodeRangeBuffer(); // move to CClassNode
if (pbuf == null) {
pbuf = new CodeRangeBuffer(); // move to CClassNode
}
final int[]p = pbuf.p;
int n = p[0];
@ -163,11 +179,17 @@ public final class CodeRangeBuffer implements Cloneable {
final int incN = low + 1 - high;
if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) {
throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
}
if (incN != 1) {
if (from > p[low * 2 + 1]) from = p[low * 2 + 1];
if (to < p[(high - 1) * 2 + 2]) to = p[(high - 1) * 2 + 2];
if (from > p[low * 2 + 1]) {
from = p[low * 2 + 1];
}
if (to < p[(high - 1) * 2 + 2]) {
to = p[(high - 1) * 2 + 2];
}
}
if (incN != 0 && high < n) {
@ -197,9 +219,8 @@ public final class CodeRangeBuffer implements Cloneable {
if (from > to) {
if (env.syntax.allowEmptyRangeInCC()) {
return pbuf;
} else {
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
return addCodeRangeToBuff(pbuf, from, to);
}
@ -218,12 +239,16 @@ public final class CodeRangeBuffer implements Cloneable {
public static CodeRangeBuffer notCodeRangeBuff(final CodeRangeBuffer bbuf) {
CodeRangeBuffer pbuf = null;
if (bbuf == null) return setAllMultiByteRange(pbuf);
if (bbuf == null) {
return setAllMultiByteRange(pbuf);
}
final int[]p = bbuf.p;
final int n = p[0];
if (n <= 0) return setAllMultiByteRange(pbuf);
if (n <= 0) {
return setAllMultiByteRange(pbuf);
}
int pre = EncodingHelper.mbcodeStartPosition();
@ -235,18 +260,26 @@ public final class CodeRangeBuffer implements Cloneable {
if (pre <= from - 1) {
pbuf = addCodeRangeToBuff(pbuf, pre, from - 1);
}
if (to == ALL_MULTI_BYTE_RANGE) break;
if (to == ALL_MULTI_BYTE_RANGE) {
break;
}
pre = to + 1;
}
if (to < ALL_MULTI_BYTE_RANGE) pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
if (to < ALL_MULTI_BYTE_RANGE) {
pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
}
return pbuf;
}
// or_code_range_buf
public static CodeRangeBuffer orCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
CodeRangeBuffer bbuf2, boolean not2) {
public static CodeRangeBuffer orCodeRangeBuff(final CodeRangeBuffer bbuf1p, final boolean not1p,
final CodeRangeBuffer bbuf2p, final boolean not2p) {
CodeRangeBuffer pbuf = null;
CodeRangeBuffer bbuf1 = bbuf1p;
CodeRangeBuffer bbuf2 = bbuf2p;
boolean not1 = not1p;
boolean not2 = not2p;
if (bbuf1 == null && bbuf2 == null) {
if (not1 || not2) {
@ -266,13 +299,11 @@ public final class CodeRangeBuffer implements Cloneable {
if (bbuf1 == null) {
if (not1) {
return setAllMultiByteRange(pbuf);
} else {
if (!not2) {
return bbuf2.clone();
} else {
return notCodeRangeBuff(bbuf2);
}
}
if (!not2) {
return bbuf2.clone();
}
return notCodeRangeBuff(bbuf2);
}
if (not1) {
@ -302,16 +333,18 @@ public final class CodeRangeBuffer implements Cloneable {
}
// and_code_range1
public static CodeRangeBuffer andCodeRange1(CodeRangeBuffer pbuf, int from1, int to1, final int[]data, final int n) {
public static CodeRangeBuffer andCodeRange1(final CodeRangeBuffer pbufp, final int from1p, final int to1p, final int[]data, final int n) {
CodeRangeBuffer pbuf = pbufp;
int from1 = from1p, to1 = to1p;
for (int i=0; i<n; i++) {
final int from2 = data[i * 2 + 1];
final int to2 = data[i * 2 + 2];
if (from2 < from1) {
if (to2 < from1) {
continue;
} else {
from1 = to2 + 1;
}
from1 = to2 + 1;
} else if (from2 <= to1) {
if (to2 < to1) {
if (from1 <= from2 - 1) {
@ -324,7 +357,9 @@ public final class CodeRangeBuffer implements Cloneable {
} else {
from1 = from2;
}
if (from1 > to1) break;
if (from1 > to1) {
break;
}
}
if (from1 <= to1) {
@ -335,15 +370,22 @@ public final class CodeRangeBuffer implements Cloneable {
}
// and_code_range_buf
public static CodeRangeBuffer andCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
CodeRangeBuffer bbuf2, boolean not2) {
public static CodeRangeBuffer andCodeRangeBuff(final CodeRangeBuffer bbuf1p, final boolean not1p,
final CodeRangeBuffer bbuf2p, final boolean not2p) {
CodeRangeBuffer pbuf = null;
CodeRangeBuffer bbuf1 = bbuf1p;
CodeRangeBuffer bbuf2 = bbuf2p;
boolean not1 = not1p, not2 = not2p;
if (bbuf1 == null) {
if (not1 && bbuf2 != null) return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
if (not1 && bbuf2 != null) {
return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
}
return null;
} else if (bbuf2 == null) {
if (not2) return bbuf1.clone();
if (not2) {
return bbuf1.clone();
}
return null;
}
@ -369,8 +411,12 @@ public final class CodeRangeBuffer implements Cloneable {
final int from2 = p2[j * 2 + 1];
final int to2 = p2[j * 2 + 2];
if (from2 > to1) break;
if (to2 < from1) continue;
if (from2 > to1) {
break;
}
if (to2 < from1) {
continue;
}
final int from = from1 > from2 ? from1 : from2;
final int to = to1 < to2 ? to1 : to2;
pbuf = addCodeRangeToBuff(pbuf, from, to);

View File

@ -53,13 +53,17 @@ abstract class Compiler implements ErrorMessages {
protected abstract void compileAltNode(ConsAltNode node);
private void compileStringRawNode(final StringNode sn) {
if (sn.length() <= 0) return;
if (sn.length() <= 0) {
return;
}
addCompileString(sn.chars, sn.p, sn.length(), false);
}
private void compileStringNode(final StringNode node) {
final StringNode sn = node;
if (sn.length() <= 0) return;
if (sn.length() <= 0) {
return;
}
final boolean ambig = sn.isAmbig();
@ -145,7 +149,9 @@ abstract class Compiler implements ErrorMessages {
}
protected final void compileTreeNTimes(final Node node, final int n) {
for (int i=0; i<n; i++) compileTree(node);
for (int i=0; i<n; i++) {
compileTree(node);
}
}
protected void newSyntaxException(final String message) {

View File

@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import java.io.PrintStream;
@SuppressWarnings("javadoc")
public interface Config {
final int CHAR_TABLE_SIZE = 256;

View File

@ -23,6 +23,7 @@ import java.util.Arrays;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
@SuppressWarnings("javadoc")
public final class EncodingHelper {
final static int NEW_LINE = 0x000a;
@ -79,14 +80,19 @@ public final class EncodingHelper {
/* onigenc_get_right_adjust_char_head_with_prev */
public static int rightAdjustCharHeadWithPrev(final int s, final IntHolder prev) {
if (prev != null) prev.value = -1; /* Sorry */
if (prev != null) {
prev.value = -1; /* Sorry */
}
return s;
}
// Encoding.stepBack
public static int stepBack(final int p, int s, int n) {
while (s != -1 && n-- > 0) {
if (s <= p) return -1;
public static int stepBack(final int p, final int sp, final int np) {
int s = sp, n = np;
while (s != -1 && n-- > 0) {
if (s <= p) {
return -1;
}
s--;
}
return s;
@ -122,7 +128,7 @@ public final class EncodingHelper {
final int upper = toUpperCase(c);
if (upper != c) {
fun.apply(c, upper, arg);
ApplyCaseFold.apply(c, upper, arg);
}
}
}
@ -133,7 +139,7 @@ public final class EncodingHelper {
final int upper = toUpperCase(c);
if (upper != c) {
fun.apply(upper, c, arg);
ApplyCaseFold.apply(upper, c, arg);
}
}
}

View File

@ -21,7 +21,6 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isSingleline;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
import jdk.nashorn.internal.runtime.regexp.joni.constants.MetaChar;
@ -53,9 +52,8 @@ class Lexer extends ScannerSupport {
if (!left()) {
if (synAllow) {
return 1; /* "....{" : OK! */
} else {
throw new SyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
}
throw new SyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
}
if (!synAllow) {
@ -83,7 +81,9 @@ class Lexer extends ScannerSupport {
}
}
if (!left()) return invalidRangeQuantifier(synAllow);
if (!left()) {
return invalidRangeQuantifier(synAllow);
}
fetch();
int up;
@ -99,25 +99,35 @@ class Lexer extends ScannerSupport {
}
if (p == prev) {
if (nonLow) return invalidRangeQuantifier(synAllow);
if (nonLow) {
return invalidRangeQuantifier(synAllow);
}
up = QuantifierNode.REPEAT_INFINITE; /* {n,} : {n,infinite} */
}
} else {
if (nonLow) return invalidRangeQuantifier(synAllow);
if (nonLow) {
return invalidRangeQuantifier(synAllow);
}
unfetch();
up = low; /* {n} : exact n times */
ret = 2; /* fixed */
}
if (!left()) return invalidRangeQuantifier(synAllow);
if (!left()) {
return invalidRangeQuantifier(synAllow);
}
fetch();
if (syntax.opEscBraceInterval()) {
if (c != syntax.metaCharTable.esc) return invalidRangeQuantifier(synAllow);
if (c != syntax.metaCharTable.esc) {
return invalidRangeQuantifier(synAllow);
}
fetch();
}
if (c != '}') return invalidRangeQuantifier(synAllow);
if (c != '}') {
return invalidRangeQuantifier(synAllow);
}
if (!isRepeatInfinite(up) && low > up) {
throw new ValueException(ERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE);
@ -134,9 +144,8 @@ class Lexer extends ScannerSupport {
if (synAllow) {
restore();
return 1;
} else {
throw new SyntaxException(ERR_INVALID_REPEAT_RANGE_PATTERN);
}
throw new SyntaxException(ERR_INVALID_REPEAT_RANGE_PATTERN);
}
@SuppressWarnings("fallthrough")
@ -218,17 +227,6 @@ class Lexer extends ScannerSupport {
}
}
private int nameEndCodePoint(final int start) {
switch(start) {
case '<':
return '>';
case '\'':
return '\'';
default:
return 0;
}
}
private void fetchTokenInCCFor_charType(final boolean flag, final int type) {
token.type = TokenType.CHAR_TYPE;
token.setPropCType(type);
@ -236,7 +234,9 @@ class Lexer extends ScannerSupport {
}
private void fetchTokenInCCFor_x() {
if (!left()) return;
if (!left()) {
return;
}
final int last = p;
if (peekIs('{') && syntax.opEscXBraceHex8()) {
@ -274,7 +274,9 @@ class Lexer extends ScannerSupport {
}
private void fetchTokenInCCFor_u() {
if (!left()) return;
if (!left()) {
return;
}
final int last = p;
if (syntax.op2EscUHex4()) {
@ -329,7 +331,9 @@ class Lexer extends ScannerSupport {
} else if (c == '-') {
token.type = TokenType.CC_RANGE;
} else if (c == syntax.metaCharTable.esc) {
if (!syntax.backSlashEscapeInCC()) return token.type;
if (!syntax.backSlashEscapeInCC()) {
return token.type;
}
if (!left()) {
throw new SyntaxException(ERR_END_PATTERN_AT_ESCAPE);
}
@ -357,10 +361,14 @@ class Lexer extends ScannerSupport {
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
break;
case 'h':
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
if (syntax.op2EscHXDigit()) {
fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
}
break;
case 'H':
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
if (syntax.op2EscHXDigit()) {
fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
}
break;
case 'x':
fetchTokenInCCFor_x();
@ -424,7 +432,9 @@ class Lexer extends ScannerSupport {
}
private void fetchTokenFor_xBrace() {
if (!left()) return;
if (!left()) {
return;
}
final int last = p;
if (peekIs('{') && syntax.opEscXBraceHex8()) {
@ -461,7 +471,9 @@ class Lexer extends ScannerSupport {
}
private void fetchTokenFor_uHex() {
if (!left()) return;
if (!left()) {
return;
}
final int last = p;
if (syntax.op2EscUHex4()) {
@ -562,79 +574,129 @@ class Lexer extends ScannerSupport {
switch(c) {
case '*':
if (syntax.opEscAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
if (syntax.opEscAsteriskZeroInf()) {
fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
}
break;
case '+':
if (syntax.opEscPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
if (syntax.opEscPlusOneInf()) {
fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
}
break;
case '?':
if (syntax.opEscQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
if (syntax.opEscQMarkZeroOne()) {
fetchTokenFor_repeat(0, 1);
}
break;
case '{':
if (syntax.opEscBraceInterval()) fetchTokenFor_openBrace();
if (syntax.opEscBraceInterval()) {
fetchTokenFor_openBrace();
}
break;
case '|':
if (syntax.opEscVBarAlt()) token.type = TokenType.ALT;
if (syntax.opEscVBarAlt()) {
token.type = TokenType.ALT;
}
break;
case '(':
if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
if (syntax.opEscLParenSubexp()) {
token.type = TokenType.SUBEXP_OPEN;
}
break;
case ')':
if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
if (syntax.opEscLParenSubexp()) {
token.type = TokenType.SUBEXP_CLOSE;
}
break;
case 'w':
if (syntax.opEscWWord()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
if (syntax.opEscWWord()) {
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
}
break;
case 'W':
if (syntax.opEscWWord()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
if (syntax.opEscWWord()) {
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
}
break;
case 'b':
if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.WORD_BOUND);
if (syntax.opEscBWordBound()) {
fetchTokenFor_anchor(AnchorType.WORD_BOUND);
}
break;
case 'B':
if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
if (syntax.opEscBWordBound()) {
fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
}
break;
case '<':
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) {
fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
}
break;
case '>':
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_END);
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) {
fetchTokenFor_anchor(AnchorType.WORD_END);
}
break;
case 's':
if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
if (syntax.opEscSWhiteSpace()) {
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
}
break;
case 'S':
if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
if (syntax.opEscSWhiteSpace()) {
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
}
break;
case 'd':
if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
if (syntax.opEscDDigit()) {
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
}
break;
case 'D':
if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
if (syntax.opEscDDigit()) {
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
}
break;
case 'h':
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
if (syntax.op2EscHXDigit()) {
fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
}
break;
case 'H':
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
if (syntax.op2EscHXDigit()) {
fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
}
break;
case 'A':
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
if (syntax.opEscAZBufAnchor()) {
fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
}
break;
case 'Z':
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
if (syntax.opEscAZBufAnchor()) {
fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
}
break;
case 'z':
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
if (syntax.opEscAZBufAnchor()) {
fetchTokenFor_anchor(AnchorType.END_BUF);
}
break;
case 'G':
if (syntax.opEscCapitalGBeginAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
if (syntax.opEscCapitalGBeginAnchor()) {
fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
}
break;
case '`':
if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
if (syntax.op2EscGnuBufAnchor()) {
fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
}
break;
case '\'':
if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
if (syntax.op2EscGnuBufAnchor()) {
fetchTokenFor_anchor(AnchorType.END_BUF);
}
break;
case 'x':
fetchTokenFor_xBrace();
@ -684,22 +746,34 @@ class Lexer extends ScannerSupport {
{
switch(c) {
case '.':
if (syntax.opDotAnyChar()) token.type = TokenType.ANYCHAR;
if (syntax.opDotAnyChar()) {
token.type = TokenType.ANYCHAR;
}
break;
case '*':
if (syntax.opAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
if (syntax.opAsteriskZeroInf()) {
fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
}
break;
case '+':
if (syntax.opPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
if (syntax.opPlusOneInf()) {
fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
}
break;
case '?':
if (syntax.opQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
if (syntax.opQMarkZeroOne()) {
fetchTokenFor_repeat(0, 1);
}
break;
case '{':
if (syntax.opBraceInterval()) fetchTokenFor_openBrace();
if (syntax.opBraceInterval()) {
fetchTokenFor_openBrace();
}
break;
case '|':
if (syntax.opVBarAlt()) token.type = TokenType.ALT;
if (syntax.opVBarAlt()) {
token.type = TokenType.ALT;
}
break;
case '(':
@ -713,9 +787,13 @@ class Lexer extends ScannerSupport {
}
fetch();
if (c == syntax.metaCharTable.esc) {
if (left()) fetch();
if (left()) {
fetch();
}
} else {
if (c == ')') break;
if (c == ')') {
break;
}
}
}
continue start; // goto start
@ -723,19 +801,29 @@ class Lexer extends ScannerSupport {
unfetch();
}
if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
if (syntax.opLParenSubexp()) {
token.type = TokenType.SUBEXP_OPEN;
}
break;
case ')':
if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
if (syntax.opLParenSubexp()) {
token.type = TokenType.SUBEXP_CLOSE;
}
break;
case '^':
if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
if (syntax.opLineAnchor()) {
fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
}
break;
case '$':
if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.END_BUF : AnchorType.END_LINE);
if (syntax.opLineAnchor()) {
fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.END_BUF : AnchorType.END_LINE);
}
break;
case '[':
if (syntax.opBracketCC()) token.type = TokenType.CC_CC_OPEN;
if (syntax.opBracketCC()) {
token.type = TokenType.CC_CC_OPEN;
}
break;
case ']':
//if (*src > env->pattern) /* /].../ is allowed. */
@ -745,7 +833,9 @@ class Lexer extends ScannerSupport {
if (Option.isExtend(env.option)) {
while (left()) {
fetch();
if (EncodingHelper.isNewLine(c)) break;
if (EncodingHelper.isNewLine(c)) {
break;
}
}
continue start; // goto start
}
@ -756,7 +846,10 @@ class Lexer extends ScannerSupport {
case '\n':
case '\r':
case '\f':
if (Option.isExtend(env.option)) continue start; // goto start
if (Option.isExtend(env.option))
{
continue start; // goto start
}
break;
default: // string
@ -798,8 +891,8 @@ class Lexer extends ScannerSupport {
}
}
protected final void syntaxWarn(final String message, final char c) {
syntaxWarn(message.replace("<%n>", Character.toString(c)));
protected final void syntaxWarn(final String message, final char ch) {
syntaxWarn(message.replace("<%n>", Character.toString(ch)));
}
protected final void syntaxWarn(final String message) {

View File

@ -21,10 +21,10 @@
package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindLongest;
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
@SuppressWarnings("javadoc")
public abstract class Matcher extends IntHolder {
protected final Regex regex;
@ -73,7 +73,9 @@ public abstract class Matcher extends IntHolder {
protected final void msaInit(final int option, final int start) {
msaOptions = option;
msaStart = start;
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = -1;
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
msaBestLen = -1;
}
}
public final int match(final int at, final int range, final int option) {
@ -83,20 +85,19 @@ public abstract class Matcher extends IntHolder {
if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
return matchAt(end /*range*/, at, prev);
} else {
return matchAt(range /*range*/, at, prev);
}
return matchAt(range /*range*/, at, prev);
}
int low, high; // these are the return values
private boolean forwardSearchRange(final char[] chars, final int str, final int end, final int s, final int range, final IntHolder lowPrev) {
private boolean forwardSearchRange(final char[] ch, final int string, final int e, final int s, final int range, final IntHolder lowPrev) {
int pprev = -1;
int p = s;
if (Config.DEBUG_SEARCH) {
Config.log.println("forward_search_range: "+
"str: " + str +
", end: " + end +
"str: " + string +
", end: " + e +
", s: " + s +
", range: " + range);
}
@ -106,7 +107,7 @@ public abstract class Matcher extends IntHolder {
}
retry:while (true) {
p = regex.searchAlgorithm.search(regex, chars, p, end, range);
p = regex.searchAlgorithm.search(regex, ch, p, e, range);
if (p != -1 && p < range) {
if (p - regex.dMin < s) {
@ -119,9 +120,9 @@ public abstract class Matcher extends IntHolder {
if (regex.subAnchor != 0) {
switch (regex.subAnchor) {
case AnchorType.BEGIN_LINE:
if (p != str) {
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
if (!EncodingHelper.isNewLine(chars, prev, end)) {
if (p != string) {
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
if (!EncodingHelper.isNewLine(ch, prev, e)) {
// goto retry_gate;
pprev = p;
p++;
@ -131,17 +132,17 @@ public abstract class Matcher extends IntHolder {
break;
case AnchorType.END_LINE:
if (p == end) {
if (p == e) {
if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
if (prev != -1 && EncodingHelper.isNewLine(chars, prev, end)) {
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
if (prev != -1 && EncodingHelper.isNewLine(ch, prev, e)) {
// goto retry_gate;
pprev = p;
p++;
continue retry;
}
}
} else if (!EncodingHelper.isNewLine(chars, p, end)) {
} else if (!EncodingHelper.isNewLine(ch, p, e)) {
//if () break;
// goto retry_gate;
pprev = p;
@ -149,6 +150,9 @@ public abstract class Matcher extends IntHolder {
continue retry;
}
break;
default:
break;
} // switch
}
@ -158,7 +162,7 @@ public abstract class Matcher extends IntHolder {
if (low > s) {
lowPrev.value = EncodingHelper.prevCharHead(s, p);
} else {
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
}
}
} else {
@ -172,7 +176,7 @@ public abstract class Matcher extends IntHolder {
}
} else {
if (lowPrev != null) {
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, low);
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, low);
}
}
}
@ -182,8 +186,8 @@ public abstract class Matcher extends IntHolder {
if (Config.DEBUG_SEARCH) {
Config.log.println("forward_search_range success: "+
"low: " + (low - str) +
", high: " + (high - str) +
"low: " + (low - string) +
", high: " + (high - string) +
", dmin: " + regex.dMin +
", dmax: " + regex.dMax);
}
@ -196,20 +200,21 @@ public abstract class Matcher extends IntHolder {
}
// low, high
private boolean backwardSearchRange(final char[] chars, final int str, final int end, final int s, int range, final int adjrange) {
range += regex.dMin;
private boolean backwardSearchRange(final char[] ch, final int string, final int e, final int s, final int range, final int adjrange) {
int r = range;
r += regex.dMin;
int p = s;
retry:while (true) {
p = regex.searchAlgorithm.searchBackward(regex, chars, range, adjrange, end, p, s, range);
p = regex.searchAlgorithm.searchBackward(regex, ch, r, adjrange, e, p, s, r);
if (p != -1) {
if (regex.subAnchor != 0) {
switch (regex.subAnchor) {
case AnchorType.BEGIN_LINE:
if (p != str) {
final int prev = EncodingHelper.prevCharHead(str, p);
if (!EncodingHelper.isNewLine(chars, prev, end)) {
if (p != string) {
final int prev = EncodingHelper.prevCharHead(string, p);
if (!EncodingHelper.isNewLine(ch, prev, e)) {
p = prev;
continue retry;
}
@ -217,21 +222,28 @@ public abstract class Matcher extends IntHolder {
break;
case AnchorType.END_LINE:
if (p == end) {
if (p == e) {
if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
final int prev = EncodingHelper.prevCharHead(adjrange, p);
if (prev == -1) return false;
if (EncodingHelper.isNewLine(chars, prev, end)) {
if (prev == -1) {
return false;
}
if (EncodingHelper.isNewLine(ch, prev, e)) {
p = prev;
continue retry;
}
}
} else if (!EncodingHelper.isNewLine(chars, p, end)) {
} else if (!EncodingHelper.isNewLine(ch, p, e)) {
p = EncodingHelper.prevCharHead(adjrange, p);
if (p == -1) return false;
if (p == -1) {
return false;
}
continue retry;
}
break;
default:
break;
} // switch
}
@ -243,14 +255,16 @@ public abstract class Matcher extends IntHolder {
if (Config.DEBUG_SEARCH) {
Config.log.println("backward_search_range: "+
"low: " + (low - str) +
", high: " + (high - str));
"low: " + (low - string) +
", high: " + (high - string));
}
return true;
}
if (Config.DEBUG_SEARCH) Config.log.println("backward_search_range: fail.");
if (Config.DEBUG_SEARCH) {
Config.log.println("backward_search_range: fail.");
}
return false;
} // while
}
@ -261,27 +275,36 @@ public abstract class Matcher extends IntHolder {
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
//range = upperRange;
if (matchAt(upperRange, s, prev) != -1) {
if (!isFindLongest(regex.options)) return true;
if (!isFindLongest(regex.options)) {
return true;
}
}
} else {
//range = upperRange;
if (matchAt(upperRange, s, prev) != -1) return true;
if (matchAt(upperRange, s, prev) != -1) {
return true;
}
}
} else {
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
if (matchAt(end, s, prev) != -1) {
//range = upperRange;
if (!isFindLongest(regex.options)) return true;
if (!isFindLongest(regex.options)) {
return true;
}
}
} else {
//range = upperRange;
if (matchAt(end, s, prev) != -1) return true;
if (matchAt(end, s, prev) != -1) {
return true;
}
}
}
return false;
}
public final int search(int start, int range, final int option) {
public final int search(final int startp, final int rangep, final int option) {
int start = startp, range = rangep;
int s, prev;
int origStart = start;
final int origRange = range;
@ -294,7 +317,9 @@ public abstract class Matcher extends IntHolder {
", range " + (range - str));
}
if (start > end || start < str) return -1;
if (start > end || start < str) {
return -1;
}
/* anchor optimize: resume search range */
if (regex.anchor != 0 && str < end) {
@ -311,7 +336,10 @@ public abstract class Matcher extends IntHolder {
} else if ((regex.anchor & AnchorType.BEGIN_BUF) != 0) {
/* search str-position only */
if (range > start) {
if (start != str) return -1; // mismatch_no_msa;
if (start != str)
{
return -1; // mismatch_no_msa;
}
range = str + 1;
} else {
if (range <= str) {
@ -324,7 +352,10 @@ public abstract class Matcher extends IntHolder {
} else if ((regex.anchor & AnchorType.END_BUF) != 0) {
minSemiEnd = maxSemiEnd = end;
// !end_buf:!
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
{
return -1; // mismatch_no_msa;
}
} else if ((regex.anchor & AnchorType.SEMI_END_BUF) != 0) {
final int preEnd = EncodingHelper.stepBack(str, end, 1);
maxSemiEnd = end;
@ -332,12 +363,18 @@ public abstract class Matcher extends IntHolder {
minSemiEnd = preEnd;
if (minSemiEnd > str && start <= minSemiEnd) {
// !goto end_buf;!
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
{
return -1; // mismatch_no_msa;
}
}
} else {
minSemiEnd = end;
// !goto end_buf;!
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
{
return -1; // mismatch_no_msa;
}
}
} else if ((regex.anchor & AnchorType.ANYCHAR_STAR_ML) != 0) {
// goto !begin_position;!
@ -359,7 +396,9 @@ public abstract class Matcher extends IntHolder {
prev = -1;
msaInit(option, start);
if (matchCheck(end, s, prev)) return match(s);
if (matchCheck(end, s, prev)) {
return match(s);
}
return mismatch();
}
return -1; // goto mismatch_no_msa;
@ -389,49 +428,62 @@ public abstract class Matcher extends IntHolder {
schRange = end;
} else {
schRange += regex.dMax;
if (schRange > end) schRange = end;
if (schRange > end) {
schRange = end;
}
}
}
if ((end - start) < regex.thresholdLength) return mismatch();
if ((end - start) < regex.thresholdLength) {
return mismatch();
}
if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
do {
if (!forwardSearchRange(chars, str, end, s, schRange, this)) return mismatch(); // low, high, lowPrev
if (!forwardSearchRange(chars, str, end, s, schRange, this)) {
return mismatch(); // low, high, lowPrev
}
if (s < low) {
s = low;
prev = value;
}
while (s <= high) {
if (matchCheck(origRange, s, prev)) return match(s); // ???
if (matchCheck(origRange, s, prev)) {
return match(s); // ???
}
prev = s;
s++;
}
} while (s < range);
}
/* check only. */
if (!forwardSearchRange(chars, str, end, s, schRange, null)) {
return mismatch();
}
} else { /* check only. */
if (!forwardSearchRange(chars, str, end, s, schRange, null)) return mismatch();
if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
do {
if (matchCheck(origRange, s, prev)) return match(s);
prev = s;
s++;
} while (s < range);
return mismatch();
}
if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
do {
if (matchCheck(origRange, s, prev)) {
return match(s);
}
prev = s;
s++;
} while (s < range);
return mismatch();
}
}
do {
if (matchCheck(origRange, s, prev)) return match(s);
if (matchCheck(origRange, s, prev)) {
return match(s);
}
prev = s;
s++;
} while (s < range);
if (s == range) { /* because empty match with /$/. */
if (matchCheck(origRange, s, prev)) return match(s);
if (matchCheck(origRange, s, prev)) {
return match(s);
}
}
} else { /* backward search */
if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
@ -450,37 +502,51 @@ public abstract class Matcher extends IntHolder {
if (regex.dMax != MinMaxLen.INFINITE_DISTANCE && (end - range) >= regex.thresholdLength) {
do {
int schStart = s + regex.dMax;
if (schStart > end) schStart = end;
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch(); // low, high
if (s > high) s = high;
if (schStart > end) {
schStart = end;
}
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange))
{
return mismatch(); // low, high
}
if (s > high) {
s = high;
}
while (s != -1 && s >= low) {
prev = EncodingHelper.prevCharHead(str, s);
if (matchCheck(origStart, s, prev)) return match(s);
if (matchCheck(origStart, s, prev)) {
return match(s);
}
s = prev;
}
} while (s >= range);
return mismatch();
} else { /* check only. */
if ((end - range) < regex.thresholdLength) return mismatch();
}
if ((end - range) < regex.thresholdLength) {
return mismatch();
}
int schStart = s;
if (regex.dMax != 0) {
if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
int schStart = s;
if (regex.dMax != 0) {
if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
schStart = end;
} else {
schStart += regex.dMax;
if (schStart > end) {
schStart = end;
} else {
schStart += regex.dMax;
if (schStart > end) {
schStart = end;
}
}
}
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch();
}
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) {
return mismatch();
}
}
do {
prev = EncodingHelper.prevCharHead(str, s);
if (matchCheck(origStart, s, prev)) return match(s);
if (matchCheck(origStart, s, prev)) {
return match(s);
}
s = prev;
} while (s >= range);
@ -488,8 +554,13 @@ public abstract class Matcher extends IntHolder {
return mismatch();
}
private boolean endBuf(int start, int range, final int minSemiEnd, final int maxSemiEnd) {
if ((maxSemiEnd - str) < regex.anchorDmin) return true; // mismatch_no_msa;
private boolean endBuf(final int startp, final int rangep, final int minSemiEnd, final int maxSemiEnd) {
int start = startp;
int range = rangep;
if ((maxSemiEnd - str) < regex.anchorDmin) {
return true; // mismatch_no_msa;
}
if (range > start) {
if ((minSemiEnd - start) > regex.anchorDmax) {
@ -502,7 +573,10 @@ public abstract class Matcher extends IntHolder {
if ((maxSemiEnd - (range - 1)) < regex.anchorDmin) {
range = maxSemiEnd - regex.anchorDmin + 1;
}
if (start >= range) return true; // mismatch_no_msa;
if (start >= range)
{
return true; // mismatch_no_msa;
}
} else {
if ((minSemiEnd - range) > regex.anchorDmax) {
range = minSemiEnd - regex.anchorDmax;
@ -510,7 +584,10 @@ public abstract class Matcher extends IntHolder {
if ((maxSemiEnd - start) < regex.anchorDmin) {
start = maxSemiEnd - regex.anchorDmin;
}
if (range > start) return true; // mismatch_no_msa;
if (range > start)
{
return true; // mismatch_no_msa;
}
}
return false;
}

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public abstract class MatcherFactory {
public abstract Matcher create(Regex regex, char[] chars, int p, int end);

View File

@ -46,24 +46,40 @@ final class MinMaxLen {
};
int distanceValue() {
if (max == INFINITE_DISTANCE) return 0;
if (max == INFINITE_DISTANCE) {
return 0;
}
final int d = max - min;
/* return dist_vals[d] * 16 / (mm->min + 12); */
return d < distValues.length ? distValues[d] : 1;
}
int compareDistanceValue(final MinMaxLen other, int v1, int v2) {
if (v2 <= 0) return -1;
if (v1 <= 0) return 1;
int compareDistanceValue(final MinMaxLen other, final int v1p, final int v2p) {
int v1 = v1p, v2 = v2p;
if (v2 <= 0) {
return -1;
}
if (v1 <= 0) {
return 1;
}
v1 *= distanceValue();
v2 *= other.distanceValue();
if (v2 > v1) return 1;
if (v2 < v1) return -1;
if (v2 > v1) {
return 1;
}
if (v2 < v1) {
return -1;
}
if (other.min < min) return 1;
if (other.min > min) return -1;
if (other.min < min) {
return 1;
}
if (other.min > min) {
return -1;
}
return 0;
}
@ -96,27 +112,33 @@ final class MinMaxLen {
}
void altMerge(final MinMaxLen other) {
if (min > other.min) min = other.min;
if (max < other.max) max = other.max;
if (min > other.min) {
min = other.min;
}
if (max < other.max) {
max = other.max;
}
}
static final int INFINITE_DISTANCE = 0x7FFFFFFF;
static int distanceAdd(final int d1, final int d2) {
if (d1 == INFINITE_DISTANCE || d2 == INFINITE_DISTANCE) {
return INFINITE_DISTANCE;
} else {
if (d1 <= INFINITE_DISTANCE - d2) return d1 + d2;
else return INFINITE_DISTANCE;
}
if (d1 <= INFINITE_DISTANCE - d2) {
return d1 + d2;
}
return INFINITE_DISTANCE;
}
static int distanceMultiply(final int d, final int m) {
if (m == 0) return 0;
if (m == 0) {
return 0;
}
if (d < INFINITE_DISTANCE / m) {
return d * m;
} else {
return INFINITE_DISTANCE;
}
return INFINITE_DISTANCE;
}
static String distanceRangeToString(final int a, final int b) {

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public final class NodeOptInfo {
final MinMaxLen length = new MinMaxLen();
final OptAnchorInfo anchor = new OptAnchorInfo();
@ -91,8 +92,12 @@ public final class NodeOptInfo {
if (other.length.max > 0) {
// TODO: make sure it is not an Oniguruma bug (casting unsigned int to int for arithmetic comparison)
int otherLengthMax = other.length.max;
if (otherLengthMax == MinMaxLen.INFINITE_DISTANCE) otherLengthMax = -1;
if (expr.length > otherLengthMax) expr.length = otherLengthMax;
if (otherLengthMax == MinMaxLen.INFINITE_DISTANCE) {
otherLengthMax = -1;
}
if (expr.length > otherLengthMax) {
expr.length = otherLengthMax;
}
if (expr.mmd.max == 0) {
exb.select(expr);
} else {

View File

@ -36,14 +36,20 @@ final class OptAnchorInfo implements AnchorType {
void concat(final OptAnchorInfo left, final OptAnchorInfo right, final int leftLength, final int rightLength) {
leftAnchor = left.leftAnchor;
if (leftLength == 0) leftAnchor |= right.leftAnchor;
if (leftLength == 0) {
leftAnchor |= right.leftAnchor;
}
rightAnchor = right.rightAnchor;
if (rightLength == 0) rightAnchor |= left.rightAnchor;
if (rightLength == 0) {
rightAnchor |= left.rightAnchor;
}
}
boolean isSet(final int anchor) {
if ((leftAnchor & anchor) != 0) return true;
if ((leftAnchor & anchor) != 0) {
return true;
}
return (rightAnchor & anchor) != 0;
}
@ -77,14 +83,30 @@ final class OptAnchorInfo implements AnchorType {
static String anchorToString(final int anchor) {
final StringBuffer s = new StringBuffer("[");
if ((anchor & AnchorType.BEGIN_BUF) !=0 ) s.append("begin-buf ");
if ((anchor & AnchorType.BEGIN_LINE) !=0 ) s.append("begin-line ");
if ((anchor & AnchorType.BEGIN_POSITION) !=0 ) s.append("begin-pos ");
if ((anchor & AnchorType.END_BUF) !=0 ) s.append("end-buf ");
if ((anchor & AnchorType.SEMI_END_BUF) !=0 ) s.append("semi-end-buf ");
if ((anchor & AnchorType.END_LINE) !=0 ) s.append("end-line ");
if ((anchor & AnchorType.ANYCHAR_STAR) !=0 ) s.append("anychar-star ");
if ((anchor & AnchorType.ANYCHAR_STAR_ML) !=0 ) s.append("anychar-star-pl ");
if ((anchor & AnchorType.BEGIN_BUF) !=0 ) {
s.append("begin-buf ");
}
if ((anchor & AnchorType.BEGIN_LINE) !=0 ) {
s.append("begin-line ");
}
if ((anchor & AnchorType.BEGIN_POSITION) !=0 ) {
s.append("begin-pos ");
}
if ((anchor & AnchorType.END_BUF) !=0 ) {
s.append("end-buf ");
}
if ((anchor & AnchorType.SEMI_END_BUF) !=0 ) {
s.append("semi-end-buf ");
}
if ((anchor & AnchorType.END_LINE) !=0 ) {
s.append("end-line ");
}
if ((anchor & AnchorType.ANYCHAR_STAR) !=0 ) {
s.append("anychar-star ");
}
if ((anchor & AnchorType.ANYCHAR_STAR_ML) !=0 ) {
s.append("anychar-star-pl ");
}
s.append("]");
return s.toString();

View File

@ -56,7 +56,9 @@ final class OptExactInfo {
void concat(final OptExactInfo other) {
if (!ignoreCase && other.ignoreCase) {
if (length >= other.length) return; /* avoid */
if (length >= other.length) {
return; /* avoid */
}
ignoreCase = true;
}
@ -65,7 +67,9 @@ final class OptExactInfo {
int i;
for (i = length; p < end;) {
if (i + 1 > OPT_EXACT_MAXLEN) break;
if (i + 1 > OPT_EXACT_MAXLEN) {
break;
}
chars[i++] = other.chars[p++];
}
@ -74,15 +78,20 @@ final class OptExactInfo {
final OptAnchorInfo tmp = new OptAnchorInfo();
tmp.concat(anchor, other.anchor, 1, 1);
if (!other.reachEnd) tmp.rightAnchor = 0;
if (!other.reachEnd) {
tmp.rightAnchor = 0;
}
anchor.copy(tmp);
}
// ?? raw is not used here
void concatStr(final char[] lchars, int p, final int end, final boolean raw) {
void concatStr(final char[] lchars, final int pp, final int end, final boolean raw) {
int i;
int p = pp;
for (i = length; p < end && i < OPT_EXACT_MAXLEN;) {
if (i + 1 > OPT_EXACT_MAXLEN) break;
if (i + 1 > OPT_EXACT_MAXLEN) {
break;
}
chars[i++] = lchars[p++];
}
@ -102,17 +111,23 @@ final class OptExactInfo {
int i;
for (i = 0; i < length && i < other.length; i++) {
if (chars[i] != other.chars[i]) break;
if (chars[i] != other.chars[i]) {
break;
}
}
if (!other.reachEnd || i<other.length || i<length) reachEnd = false;
if (!other.reachEnd || i<other.length || i<length) {
reachEnd = false;
}
length = i;
ignoreCase |= other.ignoreCase;
anchor.altMerge(other.anchor);
if (!reachEnd) anchor.rightAnchor = 0;
if (!reachEnd) {
anchor.rightAnchor = 0;
}
}
@ -130,20 +145,32 @@ final class OptExactInfo {
v2 = OptMapInfo.positionValue(chars[0] & 0xff);
v1 = OptMapInfo.positionValue(alt.chars[0] & 0xff);
if (length > 1) v1 += 5;
if (alt.length > 1) v2 += 5;
if (length > 1) {
v1 += 5;
}
if (alt.length > 1) {
v2 += 5;
}
}
if (!ignoreCase) v1 *= 2;
if (!alt.ignoreCase) v2 *= 2;
if (!ignoreCase) {
v1 *= 2;
}
if (!alt.ignoreCase) {
v2 *= 2;
}
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) {
copy(alt);
}
}
// comp_opt_exact_or_map_info
private static final int COMP_EM_BASE = 20;
int compare(final OptMapInfo m) {
if (m.value <= 0) return -1;
if (m.value <= 0) {
return -1;
}
final int ve = COMP_EM_BASE * length * (ignoreCase ? 1 : 2);
final int vm = COMP_EM_BASE * 5 * 2 / m.value;

View File

@ -31,7 +31,9 @@ final class OptMapInfo {
mmd.clear();
anchor.clear();
value = 0;
for (int i=0; i<map.length; i++) map[i] = 0;
for (int i=0; i<map.length; i++) {
map[i] = 0;
}
}
void copy(final OptMapInfo other) {
@ -50,11 +52,10 @@ final class OptMapInfo {
}
}
void addCharAmb(final char[] chars, final int p, final int end, int caseFoldFlag) {
void addCharAmb(final char[] chars, final int p, final int end, final int caseFoldFlag) {
addChar(chars[p]);
caseFoldFlag &= ~Config.INTERNAL_ENC_CASE_FOLD_MULTI_CHAR;
final char[]items = EncodingHelper.caseFoldCodesByString(caseFoldFlag, chars[p]);
final char[]items = EncodingHelper.caseFoldCodesByString(caseFoldFlag & ~Config.INTERNAL_ENC_CASE_FOLD_MULTI_CHAR, chars[p]);
for (int i=0; i<items.length; i++) {
addChar(items[i]);
@ -64,7 +65,9 @@ final class OptMapInfo {
// select_opt_map_info
private static final int z = 1<<15; /* 32768: something big value */
void select(final OptMapInfo alt) {
if (alt.value == 0) return;
if (alt.value == 0) {
return;
}
if (value == 0) {
copy(alt);
return;
@ -73,13 +76,17 @@ final class OptMapInfo {
final int v1 = z / value;
final int v2 = z /alt.value;
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) {
copy(alt);
}
}
// alt_merge_opt_map_info
void altMerge(final OptMapInfo other) {
/* if (! is_equal_mml(&to->mmd, &add->mmd)) return ; */
if (value == 0) return;
if (value == 0) {
return;
}
if (other.value == 0 || mmd.max < other.mmd.max) {
clear();
return;
@ -89,8 +96,12 @@ final class OptMapInfo {
int val = 0;
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
if (other.map[i] != 0) map[i] = 1;
if (map[i] != 0) val += positionValue(i);
if (other.map[i] != 0) {
map[i] = 1;
}
if (map[i] != 0) {
val += positionValue(i);
}
}
value = val;
@ -112,9 +123,8 @@ final class OptMapInfo {
static int positionValue(final int i) {
if (i < ByteValTable.length) {
return ByteValTable[i];
} else {
return 4; /* Take it easy. */
}
return 4; /* Take it easy. */
}
}

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public class Option {
/* options */
@ -43,19 +44,43 @@ public class Option {
public static String toString(final int option) {
String options = "";
if (isIgnoreCase(option)) options += "IGNORECASE ";
if (isExtend(option)) options += "EXTEND ";
if (isMultiline(option)) options += "MULTILINE ";
if (isSingleline(option)) options += "SINGLELINE ";
if (isFindLongest(option)) options += "FIND_LONGEST ";
if (isFindNotEmpty(option)) options += "FIND_NOT_EMPTY ";
if (isNegateSingleline(option)) options += "NEGATE_SINGLELINE ";
if (isDontCaptureGroup(option)) options += "DONT_CAPTURE_GROUP ";
if (isCaptureGroup(option)) options += "CAPTURE_GROUP ";
if (isIgnoreCase(option)) {
options += "IGNORECASE ";
}
if (isExtend(option)) {
options += "EXTEND ";
}
if (isMultiline(option)) {
options += "MULTILINE ";
}
if (isSingleline(option)) {
options += "SINGLELINE ";
}
if (isFindLongest(option)) {
options += "FIND_LONGEST ";
}
if (isFindNotEmpty(option)) {
options += "FIND_NOT_EMPTY ";
}
if (isNegateSingleline(option)) {
options += "NEGATE_SINGLELINE ";
}
if (isDontCaptureGroup(option)) {
options += "DONT_CAPTURE_GROUP ";
}
if (isCaptureGroup(option)) {
options += "CAPTURE_GROUP ";
}
if (isNotBol(option)) options += "NOTBOL ";
if (isNotEol(option)) options += "NOTEOL ";
if (isPosixRegion(option)) options += "POSIX_REGION ";
if (isNotBol(option)) {
options += "NOTBOL ";
}
if (isNotEol(option)) {
options += "NOTEOL ";
}
if (isPosixRegion(option)) {
options += "POSIX_REGION ";
}
return options;
}

View File

@ -22,7 +22,6 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnOff;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isDontCaptureGroup;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnyCharNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
@ -77,7 +76,9 @@ class Parser extends Lexer {
restore();
return true;
}
if (c == syntax.metaCharTable.esc) inEsc = true;
if (c == syntax.metaCharTable.esc) {
inEsc = true;
}
}
}
@ -165,7 +166,9 @@ class Parser extends Lexer {
arg.vIsRaw = false;
fetchTokenInCC();
fetched = true;
if (token.type == TokenType.CC_RANGE || andStart) env.ccEscWarn("-"); /* [--x] or [a&&-x] is warned. */
if (token.type == TokenType.CC_RANGE || andStart) {
env.ccEscWarn("-"); /* [--x] or [a&&-x] is warned. */
}
parseCharClassValEntry(cc, arg); // goto val_entry
break;
} else if (arg.state == CCSTATE.RANGE) {
@ -214,7 +217,9 @@ class Parser extends Lexer {
prevCC.and(cc);
} else {
prevCC = cc;
if (workCC == null) workCC = new CClassNode();
if (workCC == null) {
workCC = new CClassNode();
}
cc = workCC;
}
cc.clear();
@ -227,7 +232,9 @@ class Parser extends Lexer {
throw new InternalException(ERR_PARSER_BUG);
} // switch
if (!fetched) fetchTokenInCC();
if (!fetched) {
fetchTokenInCC();
}
} // while
@ -443,7 +450,10 @@ class Parser extends Lexer {
}
private Node parseExp(final TokenType term) {
if (token.type == term) return StringNode.EMPTY; // goto end_of_token
if (token.type == term)
{
return StringNode.EMPTY; // goto end_of_token
}
Node node = null;
boolean group = false;
@ -474,9 +484,8 @@ class Parser extends Lexer {
}
if (token.escaped) {
return parseExpTkRawByte(group); // goto tk_raw_byte
} else {
return parseExpTkByte(group); // goto tk_byte
}
return parseExpTkByte(group); // goto tk_byte
case STRING:
return parseExpTkByte(group); // tk_byte:
@ -496,7 +505,9 @@ class Parser extends Lexer {
if (Config.NON_UNICODE_SDW) {
final CClassNode cc = new CClassNode();
cc.addCType(token.getPropCType(), false, env, this);
if (token.getPropNot()) cc.setNot();
if (token.getPropNot()) {
cc.setNot();
}
node = cc;
}
break;
@ -507,7 +518,9 @@ class Parser extends Lexer {
// #ifdef USE_SHARED_CCLASS_TABLE ... #endif
final CClassNode ccn = new CClassNode();
ccn.addCType(token.getPropCType(), false, env, this);
if (token.getPropNot()) ccn.setNot();
if (token.getPropNot()) {
ccn.setNot();
}
node = ccn;
break;
@ -555,9 +568,8 @@ class Parser extends Lexer {
if (syntax.contextIndepRepeatOps()) {
if (syntax.contextInvalidRepeatOps()) {
throw new SyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED);
} else {
node = StringNode.EMPTY; // node_new_empty
}
node = StringNode.EMPTY; // node_new_empty
} else {
return parseExpTkByte(group); // goto tk_byte
}
@ -578,7 +590,9 @@ class Parser extends Lexer {
final StringNode node = new StringNode(chars, token.backP, p); // tk_byte:
while (true) {
fetchToken();
if (token.type != TokenType.STRING) break;
if (token.type != TokenType.STRING) {
break;
}
if (token.backP == node.end) {
node.end = p; // non escaped character, remain shared, just increase shared range
@ -605,7 +619,8 @@ class Parser extends Lexer {
return parseExpRepeat(node, group);
}
private Node parseExpRepeat(Node target, final boolean group) {
private Node parseExpRepeat(final Node targetp, final boolean group) {
Node target = targetp;
while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) { // repeat:
if (target.isInvalidQuantifier()) {
throw new SyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_INVALID);
@ -674,24 +689,25 @@ class Parser extends Lexer {
if (token.type == TokenType.EOT || token.type == term || token.type == TokenType.ALT) {
return node;
} else {
final ConsAltNode top = ConsAltNode.newListNode(node, null);
ConsAltNode t = top;
while (token.type != TokenType.EOT && token.type != term && token.type != TokenType.ALT) {
node = parseExp(term);
if (node.getType() == NodeType.LIST) {
t.setCdr((ConsAltNode)node);
while (((ConsAltNode)node).cdr != null ) node = ((ConsAltNode)node).cdr;
t = ((ConsAltNode)node);
} else {
t.setCdr(ConsAltNode.newListNode(node, null));
t = t.cdr;
}
}
return top;
}
final ConsAltNode top = ConsAltNode.newListNode(node, null);
ConsAltNode t = top;
while (token.type != TokenType.EOT && token.type != term && token.type != TokenType.ALT) {
node = parseExp(term);
if (node.getType() == NodeType.LIST) {
t.setCdr((ConsAltNode)node);
while (((ConsAltNode)node).cdr != null ) {
node = ((ConsAltNode)node).cdr;
}
t = ((ConsAltNode)node);
} else {
t.setCdr(ConsAltNode.newListNode(node, null));
t = t.cdr;
}
}
return top;
}
/* term_tok: TK_EOT or TK_SUBEXP_CLOSE */
@ -711,7 +727,9 @@ class Parser extends Lexer {
t = t.cdr;
}
if (token.type != term) parseSubExpError(term);
if (token.type != term) {
parseSubExpError(term);
}
return top;
} else {
parseSubExpError(term);
@ -719,12 +737,11 @@ class Parser extends Lexer {
}
}
private void parseSubExpError(final TokenType term) {
private static void parseSubExpError(final TokenType term) {
if (term == TokenType.SUBEXP_CLOSE) {
throw new SyntaxException(ERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS);
} else {
throw new InternalException(ERR_PARSER_BUG);
}
throw new InternalException(ERR_PARSER_BUG);
}
private Node parseRegexp() {

View File

@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.constants.RegexState;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
@SuppressWarnings("javadoc")
public final class Regex implements RegexState {
int[] code; /* compiled pattern */
@ -107,7 +108,8 @@ public final class Regex implements RegexState {
}
// onig_alloc_init
public Regex(final char[] chars, final int p, final int end, int option, final int caseFoldFlag, final Syntax syntax, final WarnCallback warnings) {
public Regex(final char[] chars, final int p, final int end, final int optionp, final int caseFoldFlag, final Syntax syntax, final WarnCallback warnings) {
int option = optionp;
if ((option & (Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) ==
(Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) {
@ -169,19 +171,33 @@ public final class Regex implements RegexState {
if (len < Config.CHAR_TABLE_SIZE) {
// map/skip
if (map == null) map = new byte[Config.CHAR_TABLE_SIZE];
if (map == null) {
map = new byte[Config.CHAR_TABLE_SIZE];
}
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) map[i] = (byte)len;
for (int i=0; i<len-1; i++) map[chars[p + i] & 0xff] = (byte)(len - 1 -i); // oxff ??
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
map[i] = (byte)len;
}
for (int i=0; i<len-1; i++)
{
map[chars[p + i] & 0xff] = (byte)(len - 1 -i); // oxff ??
}
} else {
if (intMap == null) intMap = new int[Config.CHAR_TABLE_SIZE];
if (intMap == null) {
intMap = new int[Config.CHAR_TABLE_SIZE];
}
for (int i=0; i<len-1; i++) intMap[chars[p + i] & 0xff] = len - 1 - i; // oxff ??
for (int i=0; i<len-1; i++)
{
intMap[chars[p + i] & 0xff] = len - 1 - i; // oxff ??
}
}
}
void setExactInfo(final OptExactInfo e) {
if (e.length == 0) return;
if (e.length == 0) {
return;
}
// shall we copy that ?
exact = e.chars;
@ -257,7 +273,11 @@ public final class Regex implements RegexState {
s.append("exact: [").append(exact, exactP, exactEnd - exactP).append("]: length: ").append(exactEnd - exactP).append("\n");
} else if (searchAlgorithm == SearchAlgorithm.MAP) {
int n=0;
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) if (map[i] != 0) n++;
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
if (map[i] != 0) {
n++;
}
}
s.append("map: n = ").append(n).append("\n");
if (n > 0) {

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public final class Region {
static final int REGION_NOTPOS = -1;
@ -36,7 +37,9 @@ public final class Region {
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Region: \n");
for (int i=0; i<beg.length; i++) sb.append(" " + i + ": (" + beg[i] + "-" + end[i] + ")");
for (int i=0; i<beg.length; i++) {
sb.append(" " + i + ": (" + beg[i] + "-" + end[i] + ")");
}
return sb.toString();
}

View File

@ -20,11 +20,11 @@
package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsClear;
import jdk.nashorn.internal.runtime.regexp.joni.ast.Node;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
@SuppressWarnings("javadoc")
public final class ScanEnvironment {
private static final int SCANENV_MEMNODES_SIZE = 8;
@ -92,7 +92,10 @@ public final class ScanEnvironment {
case 'b': return '\010';
case 'e': return '\033';
case 'v':
if (syntax.op2EscVVtab()) return 11; // ???
if (syntax.op2EscVVtab())
{
return 11; // ???
}
break;
default:
break;

View File

@ -60,7 +60,9 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
if (Character.isDigit(c)) {
final int onum = num;
num = num * 10 + EncodingHelper.digitVal(c);
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
return -1;
}
} else {
unfetch();
break;
@ -70,16 +72,19 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
return num;
}
protected final int scanUnsignedHexadecimalNumber(int maxLength) {
protected final int scanUnsignedHexadecimalNumber(final int maxLength) {
final int last = c;
int num = 0;
while(left() && maxLength-- != 0) {
int ml = maxLength;
while(left() && ml-- != 0) {
fetch();
if (EncodingHelper.isXDigit(c)) {
final int onum = num;
final int val = EncodingHelper.xdigitVal(c);
num = (num << 4) + val;
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
return -1;
}
} else {
unfetch();
break;
@ -89,16 +94,19 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
return num;
}
protected final int scanUnsignedOctalNumber(int maxLength) {
protected final int scanUnsignedOctalNumber(final int maxLength) {
final int last = c;
int num = 0;
while(left() && maxLength-- != 0) {
int ml = maxLength;
while(left() && ml-- != 0) {
fetch();
if (Character.isDigit(c) && c < '8') {
final int onum = num;
final int val = EncodingHelper.odigitVal(c);
num = (num << 3) + val;
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
return -1;
}
} else {
unfetch();
break;
@ -144,8 +152,8 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
return p < stop ? chars[p] : 0;
}
protected final boolean peekIs(final int c) {
return peek() == c;
protected final boolean peekIs(final int ch) {
return peek() == ch;
}
protected final boolean left() {

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public abstract class SearchAlgorithm {
public abstract String getName();
@ -62,7 +63,9 @@ public abstract class SearchAlgorithm {
int end = textEnd;
end -= targetEnd - targetP - 1;
if (end > textRange) end = textRange;
if (end > textRange) {
end = textRange;
}
int s = textP;
@ -71,11 +74,15 @@ public abstract class SearchAlgorithm {
int p = s + 1;
int t = targetP + 1;
while (t < targetEnd) {
if (target[t] != text[p++]) break;
if (target[t] != text[p++]) {
break;
}
t++;
}
if (t == targetEnd) return s;
if (t == targetEnd) {
return s;
}
}
s++;
}
@ -101,10 +108,14 @@ public abstract class SearchAlgorithm {
int p = s + 1;
int t = targetP + 1;
while (t < targetEnd) {
if (target[t] != text[p++]) break;
if (target[t] != text[p++]) {
break;
}
t++;
}
if (t == targetEnd) return s;
if (t == targetEnd) {
return s;
}
}
// s = enc.prevCharHead or s = s <= adjustText ? -1 : s - 1;
s--;
@ -114,10 +125,8 @@ public abstract class SearchAlgorithm {
};
public static final class SLOW_IC extends SearchAlgorithm {
private final int caseFoldFlag;
public SLOW_IC(final Regex regex) {
this.caseFoldFlag = regex.caseFoldFlag;
//empty
}
@Override
@ -134,11 +143,15 @@ public abstract class SearchAlgorithm {
int end = textEnd;
end -= targetEnd - targetP - 1;
if (end > textRange) end = textRange;
if (end > textRange) {
end = textRange;
}
int s = textP;
while (s < end) {
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
return s;
}
s++;
}
return -1;
@ -158,17 +171,21 @@ public abstract class SearchAlgorithm {
}
while (s >= textP) {
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
return s;
}
s = EncodingHelper.prevCharHead(adjustText, s);
}
return -1;
}
private boolean lowerCaseMatch(final char[] t, int tP, final int tEnd,
final char[] chars, int p, final int end) {
private static boolean lowerCaseMatch(final char[] t, final int tPp, final int tEnd,
final char[] chars, final int pp, final int end) {
while (tP < tEnd) {
if (t[tP++] != EncodingHelper.toLowerCase(chars[p++])) return false;
for (int tP = tPp, p = pp; tP < tEnd; ) {
if (t[tP++] != EncodingHelper.toLowerCase(chars[p++])) {
return false;
}
}
return true;
}
@ -188,7 +205,9 @@ public abstract class SearchAlgorithm {
final int targetEnd = regex.exactEnd;
int end = textRange + (targetEnd - targetP) - 1;
if (end > textEnd) end = textEnd;
if (end > textEnd) {
end = textEnd;
}
final int tail = targetEnd - 1;
int s = textP + (targetEnd - targetP) - 1;
@ -199,7 +218,9 @@ public abstract class SearchAlgorithm {
int t = tail;
while (text[p] == target[t]) {
if (t == targetP) return p;
if (t == targetP) {
return p;
}
p--; t--;
}
@ -211,7 +232,9 @@ public abstract class SearchAlgorithm {
int t = tail;
while (text[p] == target[t]) {
if (t == targetP) return p;
if (t == targetP) {
return p;
}
p--; t--;
}
@ -249,7 +272,9 @@ public abstract class SearchAlgorithm {
while (t < targetEnd && text[p] == target[t]) {
p++; t++;
}
if (t == targetEnd) return s;
if (t == targetEnd) {
return s;
}
s -= regex.intMapBackward[text[s] & 0xff];
}
@ -268,8 +293,12 @@ public abstract class SearchAlgorithm {
final int len = end - p;
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) skip[i] = len;
for (int i=len-1; i>0; i--) skip[chars[i] & 0xff] = i;
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
skip[i] = len;
}
for (int i=len-1; i>0; i--) {
skip[chars[i] & 0xff] = i;
}
}
};
@ -286,7 +315,9 @@ public abstract class SearchAlgorithm {
int s = textP;
while (s < textRange) {
if (text[s] > 0xff || map[text[s]] != 0) return s;
if (text[s] > 0xff || map[text[s]] != 0) {
return s;
}
s++;
}
return -1;
@ -297,9 +328,13 @@ public abstract class SearchAlgorithm {
final byte[] map = regex.map;
int s = textStart;
if (s >= textEnd) s = textEnd - 1;
if (s >= textEnd) {
s = textEnd - 1;
}
while (s >= textP) {
if (text[s] > 0xff || map[text[s]] != 0) return s;
if (text[s] > 0xff || map[text[s]] != 0) {
return s;
}
s--;
}
return -1;

View File

@ -20,7 +20,6 @@
package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAt;
import java.lang.ref.WeakReference;
import jdk.nashorn.internal.runtime.regexp.joni.constants.StackPopLevel;
import jdk.nashorn.internal.runtime.regexp.joni.constants.StackType;
@ -61,12 +60,14 @@ abstract class StackMachine extends Matcher implements StackType {
static final ThreadLocal<WeakReference<StackEntry[]>> stacks
= new ThreadLocal<WeakReference<StackEntry[]>>() {
@SuppressWarnings("unused")
@Override
protected WeakReference<StackEntry[]> initialValue() {
return new WeakReference<StackEntry[]>(allocateStack());
}
};
@SuppressWarnings("unused")
private static StackEntry[] fetchStack() {
WeakReference<StackEntry[]> ref = stacks.get();
StackEntry[] stack = ref.get();
@ -78,7 +79,9 @@ abstract class StackMachine extends Matcher implements StackType {
}
protected final void init() {
if (stack != null) pushEnsured(ALT, regex.codeLength - 1); /* bottom stack */
if (stack != null) {
pushEnsured(ALT, regex.codeLength - 1); /* bottom stack */
}
if (repeatStk != null) {
for (int i=1; i<=regex.numMem; i++) {
repeatStk[i + memStartStk] = repeatStk[i + memEndStk] = INVALID_INDEX;
@ -87,9 +90,13 @@ abstract class StackMachine extends Matcher implements StackType {
}
protected final StackEntry ensure1() {
if (stk >= stack.length) doubleStack();
if (stk >= stack.length) {
doubleStack();
}
StackEntry e = stack[stk];
if (e == null) stack[stk] = e = new StackEntry();
if (e == null) {
stack[stk] = e = new StackEntry();
}
return e;
}
@ -190,7 +197,9 @@ abstract class StackMachine extends Matcher implements StackType {
if ((e.type & MASK_MEM_END_OR_MARK) != 0 && e.getMemNum() == mnum) {
level++;
} else if (e.type == MEM_START && e.getMemNum() == mnum) {
if (level == 0) break;
if (level == 0) {
break;
}
level--;
}
}
@ -371,9 +380,8 @@ abstract class StackMachine extends Matcher implements StackType {
if (e.getNullCheckNum() == id) {
if (level == 0) {
return e.getNullCheckPStr() == s ? 1 : 0;
} else {
level--;
}
level--;
}
} else if (e.type == NULL_CHECK_END) {
level++;
@ -393,7 +401,52 @@ abstract class StackMachine extends Matcher implements StackType {
if (e.getNullCheckPStr() != s) {
isNull = 0;
break;
} else {
}
int endp;
isNull = 1;
while (k < stk) {
if (e.type == MEM_START) {
if (e.getMemEnd() == INVALID_INDEX) {
isNull = 0;
break;
}
if (bsAt(regex.btMemEnd, e.getMemNum())) {
endp = stack[e.getMemEnd()].getMemPStr();
} else {
endp = e.getMemEnd();
}
if (stack[e.getMemStart()].getMemPStr() != endp) {
isNull = 0;
break;
} else if (endp != s) {
isNull = -1; /* empty, but position changed */
}
}
k++;
e = stack[k]; // !!
}
break;
}
}
}
return isNull;
}
protected final int nullCheckMemStRec(final int id, final int s) {
int level = 0;
int k = stk;
int isNull;
while (true) {
k--;
StackEntry e = stack[k];
if (e.type == NULL_CHECK_START) {
if (e.getNullCheckNum() == id) {
if (level == 0) {
if (e.getNullCheckPStr() != s) {
isNull = 0;
break;
}
int endp;
isNull = 1;
while (k < stk) {
@ -415,62 +468,16 @@ abstract class StackMachine extends Matcher implements StackType {
}
}
k++;
e = stack[k]; // !!
e = stack[k];
}
break;
}
}
}
}
return isNull;
}
protected final int nullCheckMemStRec(final int id, final int s) {
int level = 0;
int k = stk;
int isNull;
while (true) {
k--;
StackEntry e = stack[k];
if (e.type == NULL_CHECK_START) {
if (e.getNullCheckNum() == id) {
if (level == 0) {
if (e.getNullCheckPStr() != s) {
isNull = 0;
break;
} else {
int endp;
isNull = 1;
while (k < stk) {
if (e.type == MEM_START) {
if (e.getMemEnd() == INVALID_INDEX) {
isNull = 0;
break;
}
if (bsAt(regex.btMemEnd, e.getMemNum())) {
endp = stack[e.getMemEnd()].getMemPStr();
} else {
endp = e.getMemEnd();
}
if (stack[e.getMemStart()].getMemPStr() != endp) {
isNull = 0;
break;
} else if (endp != s) {
isNull = -1; /* empty, but position changed */
}
}
k++;
e = stack[k];
}
break;
}
} else {
level--;
}
level--;
}
} else if (e.type == NULL_CHECK_END) {
if (e.getNullCheckNum() == id) level++;
if (e.getNullCheckNum() == id) {
level++;
}
}
}
return isNull;
@ -485,7 +492,9 @@ abstract class StackMachine extends Matcher implements StackType {
if (e.type == REPEAT) {
if (level == 0) {
if (e.getRepeatNum() == id) return k;
if (e.getRepeatNum() == id) {
return k;
}
}
} else if (e.type == CALL_FRAME) {
level--;
@ -505,9 +514,8 @@ abstract class StackMachine extends Matcher implements StackType {
if (e.type == CALL_FRAME) {
if (level == 0) {
return e.getCallFrameRetAddr();
} else {
level--;
}
level--;
} else if (e.type == RETURN) {
level++;
}

View File

@ -20,10 +20,10 @@
package jdk.nashorn.internal.runtime.regexp.joni;
import static jdk.nashorn.internal.runtime.regexp.joni.constants.MetaChar.INEFFECTIVE_META_CHAR;
import jdk.nashorn.internal.runtime.regexp.joni.constants.SyntaxProperties;
public final class Syntax implements SyntaxProperties{
@SuppressWarnings("javadoc")
public final class Syntax implements SyntaxProperties {
private final int op;
private final int op2;
private final int behavior;

View File

@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
/**
* @author <a href="mailto:ola.bini@gmail.com">Ola Bini</a>
*/
@SuppressWarnings("javadoc")
public interface WarnCallback {
WarnCallback DEFAULT = new WarnCallback() {
@Override

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
@SuppressWarnings("javadoc")
public interface Warnings {
final String INVALID_BACKREFERENCE = "invalid back reference";
final String INVALID_SUBEXP_CALL = "invalid subexp call";

View File

@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
@SuppressWarnings("javadoc")
public final class AnchorNode extends Node implements AnchorType {
public int type;
public Node target;
@ -65,28 +66,60 @@ public final class AnchorNode extends Node implements AnchorType {
}
public String typeToString() {
final StringBuilder type = new StringBuilder();
if (isType(BEGIN_BUF)) type.append("BEGIN_BUF ");
if (isType(BEGIN_LINE)) type.append("BEGIN_LINE ");
if (isType(BEGIN_POSITION)) type.append("BEGIN_POSITION ");
if (isType(END_BUF)) type.append("END_BUF ");
if (isType(SEMI_END_BUF)) type.append("SEMI_END_BUF ");
if (isType(END_LINE)) type.append("END_LINE ");
if (isType(WORD_BOUND)) type.append("WORD_BOUND ");
if (isType(NOT_WORD_BOUND)) type.append("NOT_WORD_BOUND ");
if (isType(WORD_BEGIN)) type.append("WORD_BEGIN ");
if (isType(WORD_END)) type.append("WORD_END ");
if (isType(PREC_READ)) type.append("PREC_READ ");
if (isType(PREC_READ_NOT)) type.append("PREC_READ_NOT ");
if (isType(LOOK_BEHIND)) type.append("LOOK_BEHIND ");
if (isType(LOOK_BEHIND_NOT)) type.append("LOOK_BEHIND_NOT ");
if (isType(ANYCHAR_STAR)) type.append("ANYCHAR_STAR ");
if (isType(ANYCHAR_STAR_ML)) type.append("ANYCHAR_STAR_ML ");
return type.toString();
final StringBuilder sb = new StringBuilder();
if (isType(BEGIN_BUF)) {
sb.append("BEGIN_BUF ");
}
if (isType(BEGIN_LINE)) {
sb.append("BEGIN_LINE ");
}
if (isType(BEGIN_POSITION)) {
sb.append("BEGIN_POSITION ");
}
if (isType(END_BUF)) {
sb.append("END_BUF ");
}
if (isType(SEMI_END_BUF)) {
sb.append("SEMI_END_BUF ");
}
if (isType(END_LINE)) {
sb.append("END_LINE ");
}
if (isType(WORD_BOUND)) {
sb.append("WORD_BOUND ");
}
if (isType(NOT_WORD_BOUND)) {
sb.append("NOT_WORD_BOUND ");
}
if (isType(WORD_BEGIN)) {
sb.append("WORD_BEGIN ");
}
if (isType(WORD_END)) {
sb.append("WORD_END ");
}
if (isType(PREC_READ)) {
sb.append("PREC_READ ");
}
if (isType(PREC_READ_NOT)) {
sb.append("PREC_READ_NOT ");
}
if (isType(LOOK_BEHIND)) {
sb.append("LOOK_BEHIND ");
}
if (isType(LOOK_BEHIND_NOT)) {
sb.append("LOOK_BEHIND_NOT ");
}
if (isType(ANYCHAR_STAR)) {
sb.append("ANYCHAR_STAR ");
}
if (isType(ANYCHAR_STAR_ML)) {
sb.append("ANYCHAR_STAR_ML ");
}
return sb.toString();
}
private boolean isType(final int type) {
return (this.type & type) != 0;
private boolean isType(final int t) {
return (this.type & t) != 0;
}
}

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.ast;
@SuppressWarnings("javadoc")
public final class AnyCharNode extends Node {
public AnyCharNode(){}

View File

@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
@SuppressWarnings("javadoc")
public final class BackRefNode extends StateNode {
public final int backRef;

View File

@ -34,6 +34,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
import jdk.nashorn.internal.runtime.regexp.joni.exception.SyntaxException;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
@SuppressWarnings("javadoc")
public final class CClassNode extends Node {
private static final int FLAG_NCCLASS_NOT = 1<<0;
private static final int FLAG_NCCLASS_SHARE = 1<<1;
@ -100,7 +101,9 @@ public final class CClassNode extends Node {
@Override
public boolean equals(final Object other) {
if (!(other instanceof CClassNode)) return false;
if (!(other instanceof CClassNode)) {
return false;
}
final CClassNode cc = (CClassNode)other;
return ctype == cc.ctype && isNot() == cc.isNot();
}
@ -110,11 +113,12 @@ public final class CClassNode extends Node {
if (Config.USE_SHARED_CCLASS_TABLE) {
int hash = 0;
hash += ctype;
if (isNot()) hash++;
if (isNot()) {
hash++;
}
return hash + (hash >> 5);
} else {
return super.hashCode();
}
return super.hashCode();
}
@Override
@ -128,10 +132,14 @@ public final class CClassNode extends Node {
}
public String flagsToString() {
final StringBuilder flags = new StringBuilder();
if (isNot()) flags.append("NOT ");
if (isShare()) flags.append("SHARE ");
return flags.toString();
final StringBuilder f = new StringBuilder();
if (isNot()) {
f.append("NOT ");
}
if (isShare()) {
f.append("SHARE ");
}
return f.toString();
}
public boolean isEmpty() {
@ -251,7 +259,7 @@ public final class CClassNode extends Node {
}
// add_ctype_to_cc_by_range // Encoding out!
public void addCTypeByRange(final int ctype, final boolean not, final int sbOut, final int mbr[]) {
public void addCTypeByRange(final int ct, final boolean not, final int sbOut, final int mbr[]) {
final int n = mbr[0];
if (!not) {
@ -294,10 +302,14 @@ public final class CClassNode extends Node {
// !goto sb_end2!, remove duplication
prev = sbOut;
for (i=0; i<n; i++) {
if (prev < mbr[2 * i + 1]) addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
if (prev < mbr[2 * i + 1]) {
addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
}
prev = mbr[i * 2 + 2] + 1;
}
if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(prev, 0x7fffffff);
if (prev < 0x7fffffff/*!!!*/) {
addCodeRangeToBuf(prev, 0x7fffffff);
}
return;
}
bs.set(j);
@ -312,22 +324,27 @@ public final class CClassNode extends Node {
// !sb_end2:!
prev = sbOut;
for (int i=0; i<n; i++) {
if (prev < mbr[2 * i + 1]) addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
if (prev < mbr[2 * i + 1]) {
addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
}
prev = mbr[i * 2 + 2] + 1;
}
if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(prev, 0x7fffffff);
if (prev < 0x7fffffff/*!!!*/) {
addCodeRangeToBuf(prev, 0x7fffffff);
}
}
}
public void addCType(int ctype, final boolean not, final ScanEnvironment env, final IntHolder sbOut) {
public void addCType(final int ctp, final boolean not, final ScanEnvironment env, final IntHolder sbOut) {
int ct = ctp;
if (Config.NON_UNICODE_SDW) {
switch(ctype) {
switch (ct) {
case CharacterType.D:
case CharacterType.S:
case CharacterType.W:
ctype ^= CharacterType.SPECIAL_MASK;
ct ^= CharacterType.SPECIAL_MASK;
if (env.syntax == Syntax.JAVASCRIPT && ctype == CharacterType.SPACE) {
if (env.syntax == Syntax.JAVASCRIPT && ct == CharacterType.SPACE) {
// \s in JavaScript includes unicode characters.
break;
}
@ -335,26 +352,32 @@ public final class CClassNode extends Node {
if (not) {
for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
// if (!ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
if ((AsciiCtypeTable[c] & (1 << ctype)) == 0) bs.set(c);
if ((AsciiCtypeTable[c] & (1 << ct)) == 0) {
bs.set(c);
}
}
addAllMultiByteRange();
} else {
for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
// if (ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
if ((AsciiCtypeTable[c] & (1 << ctype)) != 0) bs.set(c);
if ((AsciiCtypeTable[c] & (1 << ct)) != 0) {
bs.set(c);
}
}
}
return;
default:
break;
}
}
final int[] ranges = EncodingHelper.ctypeCodeRange(ctype, sbOut);
final int[] ranges = EncodingHelper.ctypeCodeRange(ct, sbOut);
if (ranges != null) {
addCTypeByRange(ctype, not, sbOut.value, ranges);
addCTypeByRange(ct, not, sbOut.value, ranges);
return;
}
switch(ctype) {
switch(ct) {
case CharacterType.ALPHA:
case CharacterType.BLANK:
case CharacterType.CNTRL:
@ -368,12 +391,16 @@ public final class CClassNode extends Node {
case CharacterType.ALNUM:
if (not) {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (!EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
if (!EncodingHelper.isCodeCType(c, ct)) {
bs.set(c);
}
}
addAllMultiByteRange();
} else {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
if (EncodingHelper.isCodeCType(c, ct)) {
bs.set(c);
}
}
}
break;
@ -382,11 +409,15 @@ public final class CClassNode extends Node {
case CharacterType.PRINT:
if (not) {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (!EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
if (!EncodingHelper.isCodeCType(c, ct)) {
bs.set(c);
}
}
} else {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
if (EncodingHelper.isCodeCType(c, ct)) {
bs.set(c);
}
}
addAllMultiByteRange();
}
@ -395,13 +426,17 @@ public final class CClassNode extends Node {
case CharacterType.WORD:
if (!not) {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (EncodingHelper.isWord(c)) bs.set(c);
if (EncodingHelper.isWord(c)) {
bs.set(c);
}
}
addAllMultiByteRange();
} else {
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
if (!EncodingHelper.isWord(c)) bs.set(c);
if (!EncodingHelper.isWord(c)) {
bs.set(c);
}
}
}
break;
@ -422,7 +457,9 @@ public final class CClassNode extends Node {
}
public void nextStateClass(final CCStateArg arg, final ScanEnvironment env) {
if (arg.state == CCSTATE.RANGE) throw new SyntaxException(ErrorMessages.ERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE);
if (arg.state == CCSTATE.RANGE) {
throw new SyntaxException(ErrorMessages.ERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE);
}
if (arg.state == CCSTATE.VALUE && arg.type != CCVALTYPE.CLASS) {
if (arg.type == CCVALTYPE.SB) {
@ -440,7 +477,9 @@ public final class CClassNode extends Node {
switch(arg.state) {
case VALUE:
if (arg.type == CCVALTYPE.SB) {
if (arg.vs > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
if (arg.vs > 0xff) {
throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
}
bs.set(arg.vs);
} else if (arg.type == CCVALTYPE.CODE_POINT) {
addCodeRange(env, arg.vs, arg.vs);
@ -450,16 +489,17 @@ public final class CClassNode extends Node {
case RANGE:
if (arg.inType == arg.type) {
if (arg.inType == CCVALTYPE.SB) {
if (arg.vs > 0xff || arg.v > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
if (arg.vs > 0xff || arg.v > 0xff) {
throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
}
if (arg.vs > arg.v) {
if (env.syntax.allowEmptyRangeInCC()) {
// goto ccs_range_end
arg.state = CCSTATE.COMPLETE;
break;
} else {
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
bs.setRange(arg.vs, arg.v);
} else {
@ -471,9 +511,8 @@ public final class CClassNode extends Node {
// goto ccs_range_end
arg.state = CCSTATE.COMPLETE;
break;
} else {
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
}
bs.setRange(arg.vs, arg.v < 0xff ? arg.v : 0xff);
addCodeRange(env, arg.vs, arg.v);
@ -509,9 +548,8 @@ public final class CClassNode extends Node {
if (isNot()) {
return !found;
} else {
return found;
}
return found;
}
// onig_is_code_in_cc

View File

@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.WarnCallback;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
@SuppressWarnings("javadoc")
public final class ConsAltNode extends Node {
public Node car;
public ConsAltNode cdr;
@ -31,9 +32,13 @@ public final class ConsAltNode extends Node {
private ConsAltNode(final Node car, final ConsAltNode cdr, final int type) {
this.car = car;
if (car != null) car.parent = this;
if (car != null) {
car.parent = this;
}
this.cdr = cdr;
if (cdr != null) cdr.parent = this;
if (cdr != null) {
cdr.parent = this;
}
this.type = type;
}
@ -46,8 +51,9 @@ public final class ConsAltNode extends Node {
return new ConsAltNode(left, right, LIST);
}
public static ConsAltNode listAdd(ConsAltNode list, final Node x) {
public static ConsAltNode listAdd(final ConsAltNode listp, final Node x) {
final ConsAltNode n = newListNode(x, null);
ConsAltNode list = listp;
if (list != null) {
while (list.cdr != null) {

View File

@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
import jdk.nashorn.internal.runtime.regexp.joni.Option;
import jdk.nashorn.internal.runtime.regexp.joni.constants.EncloseType;
@SuppressWarnings("javadoc")
public final class EncloseNode extends StateNode implements EncloseType {
public final int type; // enclose type

View File

@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.Config;
import jdk.nashorn.internal.runtime.regexp.joni.WarnCallback;
import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType;
@SuppressWarnings("javadoc")
public abstract class Node implements NodeType {
public Node parent;
@ -33,8 +34,12 @@ public abstract class Node implements NodeType {
return 1 << getType();
}
protected void setChild(final Node tgt){} // default definition
protected Node getChild(){return null;} // default definition
protected void setChild(final Node tgt) {
//empty, default definition
}
protected Node getChild() {
return null; // default definition
}
public void swap(final Node with) {
Node tmp;
@ -46,9 +51,13 @@ public abstract class Node implements NodeType {
//setChild(with.getChild());
//with.setChild(tmp);
if (parent != null) parent.setChild(with);
if (parent != null) {
parent.setChild(with);
}
if (with.parent != null) with.parent.setChild(this);
if (with.parent != null) {
with.parent.setChild(this);
}
tmp = parent;
parent = with.parent;
@ -81,16 +90,22 @@ public abstract class Node implements NodeType {
}
protected static String pad(final Object value, final int level) {
if (value == null) return "NULL";
if (value == null) {
return "NULL";
}
final StringBuilder pad = new StringBuilder(" ");
for (int i=0; i<level; i++) pad.append(pad);
for (int i=0; i<level; i++) {
pad.append(pad);
}
return value.toString().replace("\n", "\n" + pad);
}
public final boolean isInvalidQuantifier() {
if (!Config.VANILLA) return false;
if (!Config.VANILLA) {
return false;
}
ConsAltNode node;
@ -107,14 +122,18 @@ public abstract class Node implements NodeType {
case LIST:
node = (ConsAltNode)this;
do {
if (!node.car.isInvalidQuantifier()) return false;
if (!node.car.isInvalidQuantifier()) {
return false;
}
} while ((node = node.cdr) != null);
return false;
case ALT:
node = (ConsAltNode)this;
do {
if (node.car.isInvalidQuantifier()) return true;
if (node.car.isInvalidQuantifier()) {
return true;
}
} while ((node = node.cdr) != null);
break;

View File

@ -26,11 +26,11 @@ import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.Reduce
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.PQ_Q;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.P_QQ;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.QQ;
import jdk.nashorn.internal.runtime.regexp.joni.Config;
import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
@SuppressWarnings("javadoc")
public final class QuantifierNode extends StateNode {
public Node target;
@ -78,7 +78,9 @@ public final class QuantifierNode extends StateNode {
greedy = true;
targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
if (byNumber) setByNumber();
if (byNumber) {
setByNumber();
}
}
@Override
@ -136,17 +138,27 @@ public final class QuantifierNode extends StateNode {
protected int popularNum() {
if (greedy) {
if (lower == 0) {
if (upper == 1) return 0;
else if (isRepeatInfinite(upper)) return 1;
if (upper == 1) {
return 0;
} else if (isRepeatInfinite(upper)) {
return 1;
}
} else if (lower == 1) {
if (isRepeatInfinite(upper)) return 2;
if (isRepeatInfinite(upper)) {
return 2;
}
}
} else {
if (lower == 0) {
if (upper == 1) return 3;
else if (isRepeatInfinite(upper)) return 4;
if (upper == 1) {
return 3;
} else if (isRepeatInfinite(upper)) {
return 4;
}
} else if (lower == 1) {
if (isRepeatInfinite(upper)) return 5;
if (isRepeatInfinite(upper)) {
return 5;
}
}
}
return -1;
@ -171,7 +183,9 @@ public final class QuantifierNode extends StateNode {
final int pnum = popularNum();
final int cnum = other.popularNum();
if (pnum < 0 || cnum < 0) return;
if (pnum < 0 || cnum < 0) {
return;
}
switch(REDUCE_TABLE[cnum][pnum]) {
case DEL:
@ -224,6 +238,9 @@ public final class QuantifierNode extends StateNode {
case ASIS:
setTarget(other);
return;
default:
break;
}
// ??? remove the parent from target ???
other.target = null; // remove target from reduced quantifier
@ -231,7 +248,9 @@ public final class QuantifierNode extends StateNode {
@SuppressWarnings("fallthrough")
public int setQuantifier(final Node tgt, final boolean group, final ScanEnvironment env, final char[] chars, final int p, final int end) {
if (lower == 1 && upper == 1) return 1;
if (lower == 1 && upper == 1) {
return 1;
}
switch(tgt.getType()) {

View File

@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeStatus;
@SuppressWarnings("javadoc")
public abstract class StateNode extends Node implements NodeStatus {
protected int state;

View File

@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
import jdk.nashorn.internal.runtime.regexp.joni.EncodingHelper;
import jdk.nashorn.internal.runtime.regexp.joni.constants.StringType;
@SuppressWarnings("javadoc")
public final class StringNode extends Node implements StringType {
private static final int NODE_STR_MARGIN = 16;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface AnchorType {
final int BEGIN_BUF = (1<<0);
final int BEGIN_LINE = (1<<1);

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface Arguments {
final int SPECIAL = -1;
final int NON = 0;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface AsmConstants {
final int THIS = 0;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public enum CCSTATE {
VALUE,
RANGE,

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public enum CCVALTYPE {
SB,
CODE_POINT,

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface EncloseType {
final int MEMORY = 1<<0;
final int OPTION = 1<<1;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface MetaChar {
final int ESCAPE = 0;
final int ANYCHAR = 1;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface NodeStatus {
/* status bits */
final int NST_MIN_FIXED = (1<<0);

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface NodeType {
/* node type */
final int STR = 0;

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface OPCode {
final int FINISH = 0; /* matching process terminator (no more alternative) */
final int END = 1; /* pattern code terminator (success end) */

View File

@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni.constants;
@SuppressWarnings("javadoc")
public interface OPSize {
// this might be helpful for potential byte[] migration

Some files were not shown because too many files have changed in this diff Show More