From e1cbb28f3f59e4456d533b727d4fbffead76caa7 Mon Sep 17 00:00:00 2001
From: Jon Masamitsu
Date: Fri, 1 Mar 2013 10:19:29 -0800
Subject: [PATCH 001/134] 8011268: NPG: Free unused VirtualSpaceNodes
Reviewed-by: mgerdin, coleenp, johnc
---
.../share/vm/classfile/classLoaderData.cpp | 1 +
hotspot/src/share/vm/memory/metachunk.cpp | 38 +--
hotspot/src/share/vm/memory/metachunk.hpp | 18 +-
hotspot/src/share/vm/memory/metaspace.cpp | 225 ++++++++++++++++--
hotspot/src/share/vm/memory/metaspace.hpp | 3 +
5 files changed, 233 insertions(+), 52 deletions(-)
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index e20de3c252a..75b9f34f2b2 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -686,6 +686,7 @@ void ClassLoaderDataGraph::purge() {
next = purge_me->next();
delete purge_me;
}
+ Metaspace::purge();
}
// CDS support
diff --git a/hotspot/src/share/vm/memory/metachunk.cpp b/hotspot/src/share/vm/memory/metachunk.cpp
index 4cb955862a8..0ac4ced70f4 100644
--- a/hotspot/src/share/vm/memory/metachunk.cpp
+++ b/hotspot/src/share/vm/memory/metachunk.cpp
@@ -28,6 +28,7 @@
#include "utilities/copy.hpp"
#include "utilities/debug.hpp"
+class VirtualSpaceNode;
//
// Future modification
//
@@ -45,27 +46,30 @@ size_t Metachunk::_overhead =
// Metachunk methods
-Metachunk* Metachunk::initialize(MetaWord* ptr, size_t word_size) {
- // Set bottom, top, and end. Allow space for the Metachunk itself
- Metachunk* chunk = (Metachunk*) ptr;
-
- MetaWord* chunk_bottom = ptr + _overhead;
- chunk->set_bottom(ptr);
- chunk->set_top(chunk_bottom);
- MetaWord* chunk_end = ptr + word_size;
- assert(chunk_end > chunk_bottom, "Chunk must be too small");
- chunk->set_end(chunk_end);
- chunk->set_next(NULL);
- chunk->set_prev(NULL);
- chunk->set_word_size(word_size);
+Metachunk::Metachunk(size_t word_size,
+ VirtualSpaceNode* container) :
+ _word_size(word_size),
+ _bottom(NULL),
+ _end(NULL),
+ _top(NULL),
+ _next(NULL),
+ _prev(NULL),
+ _container(container)
+{
+ _bottom = (MetaWord*)this;
+ _top = (MetaWord*)this + _overhead;
+ _end = (MetaWord*)this + word_size;
#ifdef ASSERT
- size_t data_word_size = pointer_delta(chunk_end, chunk_bottom, sizeof(MetaWord));
- Copy::fill_to_words((HeapWord*) chunk_bottom, data_word_size, metadata_chunk_initialize);
+ set_is_free(false);
+ size_t data_word_size = pointer_delta(end(),
+ top(),
+ sizeof(MetaWord));
+ Copy::fill_to_words((HeapWord*) top(),
+ data_word_size,
+ metadata_chunk_initialize);
#endif
- return chunk;
}
-
MetaWord* Metachunk::allocate(size_t word_size) {
MetaWord* result = NULL;
// If available, bump the pointer to allocate.
diff --git a/hotspot/src/share/vm/memory/metachunk.hpp b/hotspot/src/share/vm/memory/metachunk.hpp
index a10cba8dbbe..ff237ab5d3f 100644
--- a/hotspot/src/share/vm/memory/metachunk.hpp
+++ b/hotspot/src/share/vm/memory/metachunk.hpp
@@ -41,10 +41,13 @@
// | | | |
// +--------------+ <- bottom ---+ ---+
+class VirtualSpaceNode;
+
class Metachunk VALUE_OBJ_CLASS_SPEC {
// link to support lists of chunks
Metachunk* _next;
Metachunk* _prev;
+ VirtualSpaceNode* _container;
MetaWord* _bottom;
MetaWord* _end;
@@ -61,29 +64,20 @@ class Metachunk VALUE_OBJ_CLASS_SPEC {
// the space.
static size_t _overhead;
- void set_bottom(MetaWord* v) { _bottom = v; }
- void set_end(MetaWord* v) { _end = v; }
- void set_top(MetaWord* v) { _top = v; }
- void set_word_size(size_t v) { _word_size = v; }
public:
-#ifdef ASSERT
- Metachunk() : _bottom(NULL), _end(NULL), _top(NULL), _is_free(false),
- _next(NULL), _prev(NULL) {}
-#else
- Metachunk() : _bottom(NULL), _end(NULL), _top(NULL),
- _next(NULL), _prev(NULL) {}
-#endif
+ Metachunk(size_t word_size , VirtualSpaceNode* container);
// Used to add a Metachunk to a list of Metachunks
void set_next(Metachunk* v) { _next = v; assert(v != this, "Boom");}
void set_prev(Metachunk* v) { _prev = v; assert(v != this, "Boom");}
+ void set_container(VirtualSpaceNode* v) { _container = v; }
MetaWord* allocate(size_t word_size);
- static Metachunk* initialize(MetaWord* ptr, size_t word_size);
// Accessors
Metachunk* next() const { return _next; }
Metachunk* prev() const { return _prev; }
+ VirtualSpaceNode* container() const { return _container; }
MetaWord* bottom() const { return _bottom; }
MetaWord* end() const { return _end; }
MetaWord* top() const { return _top; }
diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
index 1f623bf6032..3cc6d8d4959 100644
--- a/hotspot/src/share/vm/memory/metaspace.cpp
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
@@ -112,6 +112,7 @@ typedef class FreeList ChunkList;
class ChunkManager VALUE_OBJ_CLASS_SPEC {
// Free list of chunks of different sizes.
+ // SpecializedChunk
// SmallChunk
// MediumChunk
// HumongousChunk
@@ -165,6 +166,10 @@ class ChunkManager VALUE_OBJ_CLASS_SPEC {
// for special, small, medium, and humongous chunks.
static ChunkIndex list_index(size_t size);
+ // Remove the chunk from its freelist. It is
+ // expected to be on one of the _free_chunks[] lists.
+ void remove_chunk(Metachunk* chunk);
+
// Add the simple linked list of chunks to the freelist of chunks
// of type index.
void return_chunks(ChunkIndex index, Metachunk* chunks);
@@ -255,6 +260,8 @@ class VirtualSpaceNode : public CHeapObj {
ReservedSpace _rs;
VirtualSpace _virtual_space;
MetaWord* _top;
+ // count of chunks contained in this VirtualSpace
+ uintx _container_count;
// Convenience functions for logical bottom and end
MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); }
@@ -264,10 +271,19 @@ class VirtualSpaceNode : public CHeapObj {
char* low() const { return virtual_space()->low(); }
char* high() const { return virtual_space()->high(); }
+ // The first Metachunk will be allocated at the bottom of the
+ // VirtualSpace
+ Metachunk* first_chunk() { return (Metachunk*) bottom(); }
+
+ void inc_container_count();
+#ifdef ASSERT
+ uint container_count_slow();
+#endif
+
public:
VirtualSpaceNode(size_t byte_size);
- VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs) {}
+ VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs), _container_count(0) {}
~VirtualSpaceNode();
// address of next available space in _virtual_space;
@@ -288,6 +304,12 @@ class VirtualSpaceNode : public CHeapObj {
MetaWord* top() const { return _top; }
void inc_top(size_t word_size) { _top += word_size; }
+ uintx container_count() { return _container_count; }
+ void dec_container_count();
+#ifdef ASSERT
+ void verify_container_count();
+#endif
+
// used and capacity in this single entry in the list
size_t used_words_in_vs() const;
size_t capacity_words_in_vs() const;
@@ -306,6 +328,10 @@ class VirtualSpaceNode : public CHeapObj {
bool expand_by(size_t words, bool pre_touch = false);
bool shrink_by(size_t words);
+ // In preparation for deleting this node, remove all the chunks
+ // in the node from any freelist.
+ void purge(ChunkManager* chunk_manager);
+
#ifdef ASSERT
// Debug support
static void verify_virtual_space_total();
@@ -317,7 +343,7 @@ class VirtualSpaceNode : public CHeapObj {
};
// byte_size is the size of the associated virtualspace.
-VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL), _rs(0) {
+VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL), _rs(0), _container_count(0) {
// align up to vm allocation granularity
byte_size = align_size_up(byte_size, os::vm_allocation_granularity());
@@ -341,6 +367,39 @@ VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL),
MemTracker::record_virtual_memory_type((address)_rs.base(), mtClass);
}
+void VirtualSpaceNode::purge(ChunkManager* chunk_manager) {
+ Metachunk* chunk = first_chunk();
+ Metachunk* invalid_chunk = (Metachunk*) top();
+ while (chunk < invalid_chunk ) {
+ assert(chunk->is_free(), "Should be marked free");
+ MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
+ chunk_manager->remove_chunk(chunk);
+ assert(chunk->next() == NULL &&
+ chunk->prev() == NULL,
+ "Was not removed from its list");
+ chunk = (Metachunk*) next;
+ }
+}
+
+#ifdef ASSERT
+uint VirtualSpaceNode::container_count_slow() {
+ uint count = 0;
+ Metachunk* chunk = first_chunk();
+ Metachunk* invalid_chunk = (Metachunk*) top();
+ while (chunk < invalid_chunk ) {
+ MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
+ // Don't count the chunks on the free lists. Those are
+ // still part of the VirtualSpaceNode but not currently
+ // counted.
+ if (!chunk->is_free()) {
+ count++;
+ }
+ chunk = (Metachunk*) next;
+ }
+ return count;
+}
+#endif
+
// List of VirtualSpaces for metadata allocation.
// It has a _next link for singly linked list and a MemRegion
// for total space in the VirtualSpace.
@@ -410,14 +469,14 @@ class VirtualSpaceList : public CHeapObj {
void initialize(size_t word_size);
size_t virtual_space_total() { return _virtual_space_total; }
- void inc_virtual_space_total(size_t v) {
- Atomic::add_ptr(v, &_virtual_space_total);
- }
- size_t virtual_space_count() { return _virtual_space_count; }
- void inc_virtual_space_count() {
- Atomic::inc_ptr(&_virtual_space_count);
- }
+ void inc_virtual_space_total(size_t v);
+ void dec_virtual_space_total(size_t v);
+ void inc_virtual_space_count();
+ void dec_virtual_space_count();
+
+ // Unlink empty VirtualSpaceNodes and free it.
+ void purge();
// Used and capacity in the entire list of virtual spaces.
// These are global values shared by all Metaspaces
@@ -641,6 +700,28 @@ Mutex* const SpaceManager::_expand_lock =
SpaceManager::_expand_lock_name,
Mutex::_allow_vm_block_flag);
+void VirtualSpaceNode::inc_container_count() {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _container_count++;
+ assert(_container_count == container_count_slow(),
+ err_msg("Inconsistency in countainer_count _container_count " SIZE_FORMAT
+ "container_count_slow() " SIZE_FORMAT,
+ _container_count, container_count_slow()));
+}
+
+void VirtualSpaceNode::dec_container_count() {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _container_count--;
+}
+
+#ifdef ASSERT
+void VirtualSpaceNode::verify_container_count() {
+ assert(_container_count == container_count_slow(),
+ err_msg("Inconsistency in countainer_count _container_count " SIZE_FORMAT
+ "container_count_slow() " SIZE_FORMAT, _container_count, container_count_slow()));
+}
+#endif
+
// BlockFreelist methods
BlockFreelist::BlockFreelist() : _dictionary(NULL) {}
@@ -701,6 +782,10 @@ void BlockFreelist::print_on(outputStream* st) const {
VirtualSpaceNode::~VirtualSpaceNode() {
_rs.release();
+#ifdef ASSERT
+ size_t word_size = sizeof(*this) / BytesPerWord;
+ Copy::fill_to_words((HeapWord*) this, word_size, 0xf1f1f1f1);
+#endif
}
size_t VirtualSpaceNode::used_words_in_vs() const {
@@ -733,8 +818,8 @@ Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) {
// Take the space (bump top on the current virtual space).
inc_top(chunk_word_size);
- // Point the chunk at the space
- Metachunk* result = Metachunk::initialize(chunk_limit, chunk_word_size);
+ // Initialize the chunk
+ Metachunk* result = ::new (chunk_limit) Metachunk(chunk_word_size, this);
return result;
}
@@ -762,9 +847,11 @@ bool VirtualSpaceNode::shrink_by(size_t words) {
Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) {
assert_lock_strong(SpaceManager::expand_lock());
- Metachunk* result = NULL;
-
- return take_from_committed(chunk_word_size);
+ Metachunk* result = take_from_committed(chunk_word_size);
+ if (result != NULL) {
+ inc_container_count();
+ }
+ return result;
}
Metachunk* VirtualSpaceNode::get_chunk_vs_with_expand(size_t chunk_word_size) {
@@ -843,6 +930,83 @@ VirtualSpaceList::~VirtualSpaceList() {
}
}
+void VirtualSpaceList::inc_virtual_space_total(size_t v) {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _virtual_space_total = _virtual_space_total + v;
+}
+void VirtualSpaceList::dec_virtual_space_total(size_t v) {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _virtual_space_total = _virtual_space_total - v;
+}
+
+void VirtualSpaceList::inc_virtual_space_count() {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _virtual_space_count++;
+}
+void VirtualSpaceList::dec_virtual_space_count() {
+ assert_lock_strong(SpaceManager::expand_lock());
+ _virtual_space_count--;
+}
+
+void ChunkManager::remove_chunk(Metachunk* chunk) {
+ size_t word_size = chunk->word_size();
+ ChunkIndex index = list_index(word_size);
+ if (index != HumongousIndex) {
+ free_chunks(index)->remove_chunk(chunk);
+ } else {
+ humongous_dictionary()->remove_chunk(chunk);
+ }
+
+ // Chunk is being removed from the chunks free list.
+ dec_free_chunks_total(chunk->capacity_word_size());
+}
+
+// Walk the list of VirtualSpaceNodes and delete
+// nodes with a 0 container_count. Remove Metachunks in
+// the node from their respective freelists.
+void VirtualSpaceList::purge() {
+ assert_lock_strong(SpaceManager::expand_lock());
+ // Don't use a VirtualSpaceListIterator because this
+ // list is being changed and a straightforward use of an iterator is not safe.
+ VirtualSpaceNode* purged_vsl = NULL;
+ VirtualSpaceNode* prev_vsl = virtual_space_list();
+ VirtualSpaceNode* next_vsl = prev_vsl;
+ while (next_vsl != NULL) {
+ VirtualSpaceNode* vsl = next_vsl;
+ next_vsl = vsl->next();
+ // Don't free the current virtual space since it will likely
+ // be needed soon.
+ if (vsl->container_count() == 0 && vsl != current_virtual_space()) {
+ // Unlink it from the list
+ if (prev_vsl == vsl) {
+ // This is the case of the current note being the first note.
+ assert(vsl == virtual_space_list(), "Expected to be the first note");
+ set_virtual_space_list(vsl->next());
+ } else {
+ prev_vsl->set_next(vsl->next());
+ }
+
+ vsl->purge(chunk_manager());
+ dec_virtual_space_total(vsl->reserved()->word_size());
+ dec_virtual_space_count();
+ purged_vsl = vsl;
+ delete vsl;
+ } else {
+ prev_vsl = vsl;
+ }
+ }
+#ifdef ASSERT
+ if (purged_vsl != NULL) {
+ // List should be stable enough to use an iterator here.
+ VirtualSpaceListIterator iter(virtual_space_list());
+ while (iter.repeat()) {
+ VirtualSpaceNode* vsl = iter.get_next();
+ assert(vsl != purged_vsl, "Purge of vsl failed");
+ }
+ }
+#endif
+}
+
size_t VirtualSpaceList::used_words_sum() {
size_t allocated_by_vs = 0;
VirtualSpaceListIterator iter(virtual_space_list());
@@ -955,8 +1119,10 @@ Metachunk* VirtualSpaceList::get_new_chunk(size_t word_size,
// Get a chunk from the chunk freelist
Metachunk* next = chunk_manager()->chunk_freelist_allocate(grow_chunks_by_words);
- // Allocate a chunk out of the current virtual space.
- if (next == NULL) {
+ if (next != NULL) {
+ next->container()->inc_container_count();
+ } else {
+ // Allocate a chunk out of the current virtual space.
next = current_virtual_space()->get_chunk_vs(grow_chunks_by_words);
}
@@ -1567,9 +1733,6 @@ Metachunk* ChunkManager::free_chunks_get(size_t word_size) {
}
// Chunk is being removed from the chunks free list.
dec_free_chunks_total(chunk->capacity_word_size());
-#ifdef ASSERT
- chunk->set_is_free(false);
-#endif
} else {
return NULL;
}
@@ -1578,6 +1741,11 @@ Metachunk* ChunkManager::free_chunks_get(size_t word_size) {
// Remove it from the links to this freelist
chunk->set_next(NULL);
chunk->set_prev(NULL);
+#ifdef ASSERT
+ // Chunk is no longer on any freelist. Setting to false make container_count_slow()
+ // work.
+ chunk->set_is_free(false);
+#endif
slow_locked_verify();
return chunk;
}
@@ -1887,11 +2055,13 @@ void ChunkManager::return_chunks(ChunkIndex index, Metachunk* chunks) {
assert_lock_strong(SpaceManager::expand_lock());
Metachunk* cur = chunks;
- // This return chunks one at a time. If a new
+ // This returns chunks one at a time. If a new
// class List can be created that is a base class
// of FreeList then something like FreeList::prepend()
// can be used in place of this loop
while (cur != NULL) {
+ assert(cur->container() != NULL, "Container should have been set");
+ cur->container()->dec_container_count();
// Capture the next link before it is changed
// by the call to return_chunk_at_head();
Metachunk* next = cur->next();
@@ -1917,8 +2087,8 @@ SpaceManager::~SpaceManager() {
locked_print_chunks_in_use_on(gclog_or_tty);
}
- // Mangle freed memory.
- NOT_PRODUCT(mangle_freed_chunks();)
+ // Do not mangle freed Metachunks. The chunk size inside Metachunks
+ // is during the freeing of a VirtualSpaceNodes.
// Have to update before the chunks_in_use lists are emptied
// below.
@@ -1978,6 +2148,7 @@ SpaceManager::~SpaceManager() {
" granularity %d",
humongous_chunks->word_size(), HumongousChunkGranularity));
Metachunk* next_humongous_chunks = humongous_chunks->next();
+ humongous_chunks->container()->dec_container_count();
chunk_manager->humongous_dictionary()->return_chunk(humongous_chunks);
humongous_chunks = next_humongous_chunks;
}
@@ -2716,6 +2887,13 @@ Metablock* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
return Metablock::initialize(result, word_size);
}
+void Metaspace::purge() {
+ MutexLockerEx cl(SpaceManager::expand_lock(),
+ Mutex::_no_safepoint_check_flag);
+ space_list()->purge();
+ class_space_list()->purge();
+}
+
void Metaspace::print_on(outputStream* out) const {
// Print both class virtual space counts and metaspace.
if (Verbose) {
@@ -2733,7 +2911,8 @@ bool Metaspace::contains(const void * ptr) {
// aren't deleted presently. When they are, some sort of locking might
// be needed. Note, locking this can cause inversion problems with the
// caller in MetaspaceObj::is_metadata() function.
- return space_list()->contains(ptr) || class_space_list()->contains(ptr);
+ return space_list()->contains(ptr) ||
+ class_space_list()->contains(ptr);
}
void Metaspace::verify() {
diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp
index f704804795f..8d221914572 100644
--- a/hotspot/src/share/vm/memory/metaspace.hpp
+++ b/hotspot/src/share/vm/memory/metaspace.hpp
@@ -150,6 +150,9 @@ class Metaspace : public CHeapObj {
static bool contains(const void *ptr);
void dump(outputStream* const out) const;
+ // Free empty virtualspaces
+ static void purge();
+
void print_on(outputStream* st) const;
// Debugging support
void verify();
From 4e6c27cef0b04cc571efa62cc9c3eefa4a3051e6 Mon Sep 17 00:00:00 2001
From: Kevin Walls
Date: Thu, 18 Apr 2013 17:02:20 +0100
Subject: [PATCH 002/134] 7109087: gc/7072527/TestFullGCCount.java fails when
GC is set in command-line
Reviewed-by: mgerdin
---
hotspot/test/gc/7072527/TestFullGCCount.java | 92 ++++++++++----------
1 file changed, 44 insertions(+), 48 deletions(-)
diff --git a/hotspot/test/gc/7072527/TestFullGCCount.java b/hotspot/test/gc/7072527/TestFullGCCount.java
index 14a049a89b6..96b66c1e4d1 100644
--- a/hotspot/test/gc/7072527/TestFullGCCount.java
+++ b/hotspot/test/gc/7072527/TestFullGCCount.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2013, 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
@@ -25,71 +25,67 @@
* @test TestFullGCount.java
* @bug 7072527
* @summary CMS: JMM GC counters overcount in some cases
- * @run main/othervm -XX:+UseConcMarkSweepGC TestFullGCCount
- *
+ * @run main/othervm -XX:+PrintGC TestFullGCCount
*/
import java.util.*;
import java.lang.management.*;
+/*
+ * Originally for a specific failure in CMS, this test now monitors all
+ * collectors for double-counting of collections.
+ */
public class TestFullGCCount {
- public String collectorName = "ConcurrentMarkSweep";
+ static List collectors = ManagementFactory.getGarbageCollectorMXBeans();
- public static void main(String [] args) {
-
- TestFullGCCount t = null;
- if (args.length==2) {
- t = new TestFullGCCount(args[0], args[1]);
- } else {
- t = new TestFullGCCount();
- }
- System.out.println("Monitoring collector: " + t.collectorName);
- t.run();
- }
-
- public TestFullGCCount(String pool, String collector) {
- collectorName = collector;
- }
-
- public TestFullGCCount() {
- }
-
- public void run() {
- int count = 0;
+ public static void main(String[] args) {
int iterations = 20;
- long counts[] = new long[iterations];
- boolean diffAlways2 = true; // assume we will fail
+ boolean failed = false;
+ String errorMessage = "";
+ HashMap counts = new HashMap();
- for (int i=0; i(iterations));
+ }
+
+ // Perform some gc, record collector counts.
+ for (int i = 0; i < iterations; i++) {
System.gc();
- counts[i] = getCollectionCount();
- if (i>0) {
- if (counts[i] - counts[i-1] != 2) {
- diffAlways2 = false;
+ addCollectionCount(counts, i);
+ }
+
+ // Check the increments:
+ // Old gen collectors should increase by one,
+ // New collectors may or may not increase.
+ // Any increase >=2 is unexpected.
+ for (String collector : counts.keySet()) {
+ System.out.println("Checking: " + collector);
+
+ for (int i = 0; i < iterations - 1; i++) {
+ List theseCounts = counts.get(collector);
+ long a = theseCounts.get(i);
+ long b = theseCounts.get(i + 1);
+ if (b - a >= 2) {
+ failed = true;
+ errorMessage += "Collector '" + collector + "' has increment " + (b - a) +
+ " at iteration " + i + "\n";
}
}
}
- if (diffAlways2) {
- throw new RuntimeException("FAILED: System.gc must be incrementing count twice.");
+ if (failed) {
+ System.err.println(errorMessage);
+ throw new RuntimeException("FAILED: System.gc collections miscounted.");
}
System.out.println("Passed.");
}
- private long getCollectionCount() {
- long count = 0;
- List pools = ManagementFactory.getMemoryPoolMXBeans();
- List collectors = ManagementFactory.getGarbageCollectorMXBeans();
- for (int i=0; i counts, int iteration) {
+ for (int i = 0; i < collectors.size(); i++) {
GarbageCollectorMXBean collector = collectors.get(i);
- String name = collector.getName();
- if (name.contains(collectorName)) {
- System.out.println(name + ": collection count = "
- + collector.getCollectionCount());
- count = collector.getCollectionCount();
- }
+ List thisList = counts.get(collector.getName());
+ thisList.add(collector.getCollectionCount());
}
- return count;
}
-
}
-
From bdf829cf3edbb691de18ad231501622dc8c749a5 Mon Sep 17 00:00:00 2001
From: Stefan Karlsson
Date: Mon, 22 Apr 2013 20:27:36 +0200
Subject: [PATCH 003/134] 8012687: Remove unused is_root checks and closures
Reviewed-by: tschatzl, jmasa
---
.../gc_implementation/g1/g1CollectedHeap.cpp | 5 +--
.../gc_implementation/g1/g1CollectedHeap.hpp | 3 +-
.../vm/gc_implementation/g1/g1MarkSweep.cpp | 7 ++--
.../parallelScavenge/psMarkSweep.cpp | 29 +++++++-------
.../parallelScavenge/psMarkSweep.hpp | 1 -
.../parallelScavenge/psParallelCompact.cpp | 40 +++++++++----------
.../parallelScavenge/psParallelCompact.hpp | 25 +-----------
.../vm/gc_implementation/shared/markSweep.cpp | 9 ++---
.../vm/gc_implementation/shared/markSweep.hpp | 11 +----
.../shared/markSweep.inline.hpp | 2 +-
.../src/share/vm/memory/genCollectedHeap.cpp | 5 +--
.../src/share/vm/memory/genCollectedHeap.hpp | 3 +-
hotspot/src/share/vm/memory/genMarkSweep.cpp | 12 +++---
hotspot/src/share/vm/memory/sharedHeap.cpp | 7 ++--
hotspot/src/share/vm/memory/sharedHeap.hpp | 3 +-
15 files changed, 59 insertions(+), 103 deletions(-)
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index 85bc31b8a53..4c1b75133b8 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -5079,10 +5079,9 @@ g1_process_strong_roots(bool is_scavenging,
}
void
-G1CollectedHeap::g1_process_weak_roots(OopClosure* root_closure,
- OopClosure* non_root_closure) {
+G1CollectedHeap::g1_process_weak_roots(OopClosure* root_closure) {
CodeBlobToOopClosure roots_in_blobs(root_closure, /*do_marking=*/ false);
- SharedHeap::process_weak_roots(root_closure, &roots_in_blobs, non_root_closure);
+ SharedHeap::process_weak_roots(root_closure, &roots_in_blobs);
}
// Weak Reference Processing support
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
index 4fbf0ff367a..9080bc353cb 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
@@ -827,8 +827,7 @@ protected:
// Apply "blk" to all the weak roots of the system. These include
// JNI weak roots, the code cache, system dictionary, symbol table,
// string table, and referents of reachable weak refs.
- void g1_process_weak_roots(OopClosure* root_closure,
- OopClosure* non_root_closure);
+ void g1_process_weak_roots(OopClosure* root_closure);
// Frees a non-humongous region by initializing its contents and
// adding it to the free list that's passed as a parameter (this is
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
index b987f7df4e7..9fa3dfb273c 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
@@ -308,17 +308,16 @@ void G1MarkSweep::mark_sweep_phase3() {
sh->process_strong_roots(true, // activate StrongRootsScope
false, // not scavenging.
SharedHeap::SO_AllClasses,
- &GenMarkSweep::adjust_root_pointer_closure,
+ &GenMarkSweep::adjust_pointer_closure,
NULL, // do not touch code cache here
&GenMarkSweep::adjust_klass_closure);
assert(GenMarkSweep::ref_processor() == g1h->ref_processor_stw(), "Sanity");
- g1h->ref_processor_stw()->weak_oops_do(&GenMarkSweep::adjust_root_pointer_closure);
+ g1h->ref_processor_stw()->weak_oops_do(&GenMarkSweep::adjust_pointer_closure);
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
- g1h->g1_process_weak_roots(&GenMarkSweep::adjust_root_pointer_closure,
- &GenMarkSweep::adjust_pointer_closure);
+ g1h->g1_process_weak_roots(&GenMarkSweep::adjust_pointer_closure);
GenMarkSweep::adjust_marks();
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
index 63cd3760282..cf07854cd9c 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
@@ -583,28 +583,27 @@ void PSMarkSweep::mark_sweep_phase3() {
ClassLoaderDataGraph::clear_claimed_marks();
// General strong roots.
- Universe::oops_do(adjust_root_pointer_closure());
- JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
- CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure());
- Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL);
- ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
- FlatProfiler::oops_do(adjust_root_pointer_closure());
- Management::oops_do(adjust_root_pointer_closure());
- JvmtiExport::oops_do(adjust_root_pointer_closure());
+ Universe::oops_do(adjust_pointer_closure());
+ JNIHandles::oops_do(adjust_pointer_closure()); // Global (strong) JNI handles
+ CLDToOopClosure adjust_from_cld(adjust_pointer_closure());
+ Threads::oops_do(adjust_pointer_closure(), &adjust_from_cld, NULL);
+ ObjectSynchronizer::oops_do(adjust_pointer_closure());
+ FlatProfiler::oops_do(adjust_pointer_closure());
+ Management::oops_do(adjust_pointer_closure());
+ JvmtiExport::oops_do(adjust_pointer_closure());
// SO_AllClasses
- SystemDictionary::oops_do(adjust_root_pointer_closure());
- ClassLoaderDataGraph::oops_do(adjust_root_pointer_closure(), adjust_klass_closure(), true);
- //CodeCache::scavenge_root_nmethods_oops_do(adjust_root_pointer_closure());
+ SystemDictionary::oops_do(adjust_pointer_closure());
+ ClassLoaderDataGraph::oops_do(adjust_pointer_closure(), adjust_klass_closure(), true);
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
// Global (weak) JNI handles
- JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure());
+ JNIHandles::weak_oops_do(&always_true, adjust_pointer_closure());
CodeCache::oops_do(adjust_pointer_closure());
- StringTable::oops_do(adjust_root_pointer_closure());
- ref_processor()->weak_oops_do(adjust_root_pointer_closure());
- PSScavenge::reference_processor()->weak_oops_do(adjust_root_pointer_closure());
+ StringTable::oops_do(adjust_pointer_closure());
+ ref_processor()->weak_oops_do(adjust_pointer_closure());
+ PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure());
adjust_marks();
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp
index fcbc103dc3a..7d96afbb4df 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp
@@ -44,7 +44,6 @@ class PSMarkSweep : public MarkSweep {
static KlassClosure* follow_klass_closure() { return &MarkSweep::follow_klass_closure; }
static VoidClosure* follow_stack_closure() { return (VoidClosure*)&MarkSweep::follow_stack_closure; }
static OopClosure* adjust_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_pointer_closure; }
- static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_root_pointer_closure; }
static KlassClosure* adjust_klass_closure() { return &MarkSweep::adjust_klass_closure; }
static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&MarkSweep::is_alive; }
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
index 487a4e56553..d0d50a7f699 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
@@ -787,12 +787,11 @@ bool PSParallelCompact::IsAliveClosure::do_object_b(oop p) { return mark_bitmap(
void PSParallelCompact::KeepAliveClosure::do_oop(oop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); }
void PSParallelCompact::KeepAliveClosure::do_oop(narrowOop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); }
-PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_root_pointer_closure(true);
-PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure(false);
+PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure;
PSParallelCompact::AdjustKlassClosure PSParallelCompact::_adjust_klass_closure;
-void PSParallelCompact::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p, _is_root); }
-void PSParallelCompact::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); }
+void PSParallelCompact::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p); }
+void PSParallelCompact::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p); }
void PSParallelCompact::FollowStackClosure::do_void() { _compaction_manager->follow_marking_stacks(); }
@@ -805,7 +804,7 @@ void PSParallelCompact::FollowKlassClosure::do_klass(Klass* klass) {
klass->oops_do(_mark_and_push_closure);
}
void PSParallelCompact::AdjustKlassClosure::do_klass(Klass* klass) {
- klass->oops_do(&PSParallelCompact::_adjust_root_pointer_closure);
+ klass->oops_do(&PSParallelCompact::_adjust_pointer_closure);
}
void PSParallelCompact::post_initialize() {
@@ -2398,7 +2397,7 @@ void PSParallelCompact::follow_class_loader(ParCompactionManager* cm,
void PSParallelCompact::adjust_class_loader(ParCompactionManager* cm,
ClassLoaderData* cld) {
- cld->oops_do(PSParallelCompact::adjust_root_pointer_closure(),
+ cld->oops_do(PSParallelCompact::adjust_pointer_closure(),
PSParallelCompact::adjust_klass_closure(),
true);
}
@@ -2419,32 +2418,31 @@ void PSParallelCompact::adjust_roots() {
ClassLoaderDataGraph::clear_claimed_marks();
// General strong roots.
- Universe::oops_do(adjust_root_pointer_closure());
- JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
- CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure());
- Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL);
- ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
- FlatProfiler::oops_do(adjust_root_pointer_closure());
- Management::oops_do(adjust_root_pointer_closure());
- JvmtiExport::oops_do(adjust_root_pointer_closure());
+ Universe::oops_do(adjust_pointer_closure());
+ JNIHandles::oops_do(adjust_pointer_closure()); // Global (strong) JNI handles
+ CLDToOopClosure adjust_from_cld(adjust_pointer_closure());
+ Threads::oops_do(adjust_pointer_closure(), &adjust_from_cld, NULL);
+ ObjectSynchronizer::oops_do(adjust_pointer_closure());
+ FlatProfiler::oops_do(adjust_pointer_closure());
+ Management::oops_do(adjust_pointer_closure());
+ JvmtiExport::oops_do(adjust_pointer_closure());
// SO_AllClasses
- SystemDictionary::oops_do(adjust_root_pointer_closure());
- ClassLoaderDataGraph::oops_do(adjust_root_pointer_closure(), adjust_klass_closure(), true);
+ SystemDictionary::oops_do(adjust_pointer_closure());
+ ClassLoaderDataGraph::oops_do(adjust_pointer_closure(), adjust_klass_closure(), true);
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
// Global (weak) JNI handles
- JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure());
+ JNIHandles::weak_oops_do(&always_true, adjust_pointer_closure());
CodeCache::oops_do(adjust_pointer_closure());
- StringTable::oops_do(adjust_root_pointer_closure());
- ref_processor()->weak_oops_do(adjust_root_pointer_closure());
+ StringTable::oops_do(adjust_pointer_closure());
+ ref_processor()->weak_oops_do(adjust_pointer_closure());
// Roots were visited so references into the young gen in roots
// may have been scanned. Process them also.
// Should the reference processor have a span that excludes
// young gen objects?
- PSScavenge::reference_processor()->weak_oops_do(
- adjust_root_pointer_closure());
+ PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure());
}
void PSParallelCompact::enqueue_region_draining_tasks(GCTaskQueue* q,
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
index 68b54fb9b13..6ced655c21a 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
@@ -799,16 +799,6 @@ class PSParallelCompact : AllStatic {
virtual void do_oop(narrowOop* p);
};
- // Current unused
- class FollowRootClosure: public OopsInGenClosure {
- private:
- ParCompactionManager* _compaction_manager;
- public:
- FollowRootClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
- };
-
class FollowStackClosure: public VoidClosure {
private:
ParCompactionManager* _compaction_manager;
@@ -818,10 +808,7 @@ class PSParallelCompact : AllStatic {
};
class AdjustPointerClosure: public OopClosure {
- private:
- bool _is_root;
public:
- AdjustPointerClosure(bool is_root) : _is_root(is_root) { }
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
// do not walk from thread stacks to the code cache on this phase
@@ -838,7 +825,6 @@ class PSParallelCompact : AllStatic {
friend class AdjustPointerClosure;
friend class AdjustKlassClosure;
friend class FollowKlassClosure;
- friend class FollowRootClosure;
friend class InstanceClassLoaderKlass;
friend class RefProcTaskProxy;
@@ -853,7 +839,6 @@ class PSParallelCompact : AllStatic {
static IsAliveClosure _is_alive_closure;
static SpaceInfo _space_info[last_space_id];
static bool _print_phases;
- static AdjustPointerClosure _adjust_root_pointer_closure;
static AdjustPointerClosure _adjust_pointer_closure;
static AdjustKlassClosure _adjust_klass_closure;
@@ -889,9 +874,6 @@ class PSParallelCompact : AllStatic {
static void marking_phase(ParCompactionManager* cm,
bool maximum_heap_compaction);
- template static inline void adjust_pointer(T* p, bool is_root);
- static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); }
-
template
static inline void follow_root(ParCompactionManager* cm, T* p);
@@ -1046,7 +1028,6 @@ class PSParallelCompact : AllStatic {
// Closure accessors
static OopClosure* adjust_pointer_closure() { return (OopClosure*)&_adjust_pointer_closure; }
- static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&_adjust_root_pointer_closure; }
static KlassClosure* adjust_klass_closure() { return (KlassClosure*)&_adjust_klass_closure; }
static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&_is_alive_closure; }
@@ -1067,6 +1048,7 @@ class PSParallelCompact : AllStatic {
// Check mark and maybe push on marking stack
template static inline void mark_and_push(ParCompactionManager* cm,
T* p);
+ template static inline void adjust_pointer(T* p);
static void follow_klass(ParCompactionManager* cm, Klass* klass);
static void adjust_klass(ParCompactionManager* cm, Klass* klass);
@@ -1151,9 +1133,6 @@ class PSParallelCompact : AllStatic {
static ParMarkBitMap* mark_bitmap() { return &_mark_bitmap; }
static ParallelCompactData& summary_data() { return _summary_data; }
- static inline void adjust_pointer(oop* p) { adjust_pointer(p, false); }
- static inline void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); }
-
// Reference Processing
static ReferenceProcessor* const ref_processor() { return _ref_processor; }
@@ -1230,7 +1209,7 @@ inline void PSParallelCompact::mark_and_push(ParCompactionManager* cm, T* p) {
}
template
-inline void PSParallelCompact::adjust_pointer(T* p, bool isroot) {
+inline void PSParallelCompact::adjust_pointer(T* p) {
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp
index 6ea4097daa9..5e52aa1eb84 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp
@@ -81,7 +81,7 @@ void MarkSweep::follow_class_loader(ClassLoaderData* cld) {
}
void MarkSweep::adjust_class_loader(ClassLoaderData* cld) {
- cld->oops_do(&MarkSweep::adjust_root_pointer_closure, &MarkSweep::adjust_klass_closure, true);
+ cld->oops_do(&MarkSweep::adjust_pointer_closure, &MarkSweep::adjust_klass_closure, true);
}
@@ -121,11 +121,10 @@ void MarkSweep::preserve_mark(oop obj, markOop mark) {
}
}
-MarkSweep::AdjustPointerClosure MarkSweep::adjust_root_pointer_closure(true);
-MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure(false);
+MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure;
-void MarkSweep::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p, _is_root); }
-void MarkSweep::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); }
+void MarkSweep::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p); }
+void MarkSweep::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p); }
void MarkSweep::adjust_marks() {
assert( _preserved_oop_stack.size() == _preserved_mark_stack.size(),
diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp
index 1de7561ce55..ec724afa5ec 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp
@@ -80,10 +80,7 @@ class MarkSweep : AllStatic {
};
class AdjustPointerClosure: public OopsInGenClosure {
- private:
- bool _is_root;
public:
- AdjustPointerClosure(bool is_root) : _is_root(is_root) {}
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
};
@@ -146,7 +143,6 @@ class MarkSweep : AllStatic {
static MarkAndPushClosure mark_and_push_closure;
static FollowKlassClosure follow_klass_closure;
static FollowStackClosure follow_stack_closure;
- static AdjustPointerClosure adjust_root_pointer_closure;
static AdjustPointerClosure adjust_pointer_closure;
static AdjustKlassClosure adjust_klass_closure;
@@ -179,12 +175,7 @@ class MarkSweep : AllStatic {
static void adjust_marks(); // Adjust the pointers in the preserved marks table
static void restore_marks(); // Restore the marks that we saved in preserve_mark
- template static inline void adjust_pointer(T* p, bool isroot);
-
- static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); }
- static void adjust_pointer(oop* p) { adjust_pointer(p, false); }
- static void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); }
-
+ template static inline void adjust_pointer(T* p);
};
class PreservedMark VALUE_OBJ_CLASS_SPEC {
diff --git a/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp
index 9752291959a..8ffe0f78236 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp
@@ -76,7 +76,7 @@ void MarkSweep::push_objarray(oop obj, size_t index) {
_objarray_stack.push(task);
}
-template inline void MarkSweep::adjust_pointer(T* p, bool isroot) {
+template inline void MarkSweep::adjust_pointer(T* p) {
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
index f39631ae00b..a04eb3cb721 100644
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
@@ -633,9 +633,8 @@ gen_process_strong_roots(int level,
}
void GenCollectedHeap::gen_process_weak_roots(OopClosure* root_closure,
- CodeBlobClosure* code_roots,
- OopClosure* non_root_closure) {
- SharedHeap::process_weak_roots(root_closure, code_roots, non_root_closure);
+ CodeBlobClosure* code_roots) {
+ SharedHeap::process_weak_roots(root_closure, code_roots);
// "Local" "weak" refs
for (int i = 0; i < _n_gens; i++) {
_gens[i]->ref_processor()->weak_oops_do(root_closure);
diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.hpp b/hotspot/src/share/vm/memory/genCollectedHeap.hpp
index 034511b9b55..783cd372d7c 100644
--- a/hotspot/src/share/vm/memory/genCollectedHeap.hpp
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.hpp
@@ -432,8 +432,7 @@ public:
// JNI weak roots, the code cache, system dictionary, symbol table,
// string table, and referents of reachable weak refs.
void gen_process_weak_roots(OopClosure* root_closure,
- CodeBlobClosure* code_roots,
- OopClosure* non_root_closure);
+ CodeBlobClosure* code_roots);
// Set the saved marks of generations, if that makes sense.
// In particular, if any generation might iterate over the oops
diff --git a/hotspot/src/share/vm/memory/genMarkSweep.cpp b/hotspot/src/share/vm/memory/genMarkSweep.cpp
index 2180f63886f..3fe04303263 100644
--- a/hotspot/src/share/vm/memory/genMarkSweep.cpp
+++ b/hotspot/src/share/vm/memory/genMarkSweep.cpp
@@ -282,11 +282,10 @@ void GenMarkSweep::mark_sweep_phase3(int level) {
// Need new claim bits for the pointer adjustment tracing.
ClassLoaderDataGraph::clear_claimed_marks();
- // Because the two closures below are created statically, cannot
+ // Because the closure below is created statically, we cannot
// use OopsInGenClosure constructor which takes a generation,
// as the Universe has not been created when the static constructors
// are run.
- adjust_root_pointer_closure.set_orig_generation(gch->get_gen(level));
adjust_pointer_closure.set_orig_generation(gch->get_gen(level));
gch->gen_process_strong_roots(level,
@@ -294,18 +293,17 @@ void GenMarkSweep::mark_sweep_phase3(int level) {
true, // activate StrongRootsScope
false, // not scavenging
SharedHeap::SO_AllClasses,
- &adjust_root_pointer_closure,
+ &adjust_pointer_closure,
false, // do not walk code
- &adjust_root_pointer_closure,
+ &adjust_pointer_closure,
&adjust_klass_closure);
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
CodeBlobToOopClosure adjust_code_pointer_closure(&adjust_pointer_closure,
/*do_marking=*/ false);
- gch->gen_process_weak_roots(&adjust_root_pointer_closure,
- &adjust_code_pointer_closure,
- &adjust_pointer_closure);
+ gch->gen_process_weak_roots(&adjust_pointer_closure,
+ &adjust_code_pointer_closure);
adjust_marks();
GenAdjustPointersClosure blk;
diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp
index caef7ac7ad0..cd577d4b57e 100644
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp
@@ -218,14 +218,13 @@ public:
static AlwaysTrueClosure always_true;
void SharedHeap::process_weak_roots(OopClosure* root_closure,
- CodeBlobClosure* code_roots,
- OopClosure* non_root_closure) {
+ CodeBlobClosure* code_roots) {
// Global (weak) JNI handles
JNIHandles::weak_oops_do(&always_true, root_closure);
CodeCache::blobs_do(code_roots);
- StringTable::oops_do(root_closure);
- }
+ StringTable::oops_do(root_closure);
+}
void SharedHeap::set_barrier_set(BarrierSet* bs) {
_barrier_set = bs;
diff --git a/hotspot/src/share/vm/memory/sharedHeap.hpp b/hotspot/src/share/vm/memory/sharedHeap.hpp
index 2f8e2d910c2..b13bf15b846 100644
--- a/hotspot/src/share/vm/memory/sharedHeap.hpp
+++ b/hotspot/src/share/vm/memory/sharedHeap.hpp
@@ -249,8 +249,7 @@ public:
// JNI weak roots, the code cache, system dictionary, symbol table,
// string table.
void process_weak_roots(OopClosure* root_closure,
- CodeBlobClosure* code_roots,
- OopClosure* non_root_closure);
+ CodeBlobClosure* code_roots);
// The functions below are helper functions that a subclass of
// "SharedHeap" can use in the implementation of its virtual
From ddbf6ad621d46180942e88f44cca8e59a9a432da Mon Sep 17 00:00:00 2001
From: Jon Masamitsu
Date: Mon, 22 Apr 2013 22:00:03 -0700
Subject: [PATCH 004/134] 8012111: Remove warning about CMS generation
shrinking
Reviewed-by: johnc, brutisso, stefank
---
.../concurrentMarkSweepGeneration.cpp | 5 +-
.../GuardShrinkWarning.java | 60 +++++++++++++++++++
2 files changed, 63 insertions(+), 2 deletions(-)
create mode 100644 hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java
diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
index 70a26089437..ff001185846 100644
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
@@ -3426,8 +3426,9 @@ bool ConcurrentMarkSweepGeneration::grow_to_reserved() {
void ConcurrentMarkSweepGeneration::shrink_free_list_by(size_t bytes) {
assert_locked_or_safepoint(Heap_lock);
assert_lock_strong(freelistLock());
- // XXX Fix when compaction is implemented.
- warning("Shrinking of CMS not yet implemented");
+ if (PrintGCDetails && Verbose) {
+ warning("Shrinking of CMS not yet implemented");
+ }
return;
}
diff --git a/hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java b/hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java
new file mode 100644
index 00000000000..a2d47625713
--- /dev/null
+++ b/hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test GuardShrinkWarning
+ * @summary Remove warning about CMS generation shrinking.
+ * @bug 8012111
+ * @key gc
+ * @key regression
+ * @library /testlibrary
+ * @run main/othervm GuardShrinkWarning
+ * @author jon.masamitsu@oracle.com
+ */
+
+import com.oracle.java.testlibrary.*;
+
+public class GuardShrinkWarning {
+ public static void main(String args[]) throws Exception {
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-showversion",
+ "-XX:+UseConcMarkSweepGC",
+ "-XX:+ExplicitGCInvokesConcurrent",
+ "GuardShrinkWarning$SystemGCCaller"
+ );
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ output.shouldNotContain("Shrinking of CMS not yet implemented");
+
+ output.shouldNotContain("error");
+
+ output.shouldHaveExitValue(0);
+ }
+ static class SystemGCCaller {
+ public static void main(String [] args) {
+ System.gc();
+ }
+ }
+}
From c47ec9b4ca00fe6b448c832682b73e8343503c1c Mon Sep 17 00:00:00 2001
From: Mikael Gerdin
Date: Tue, 23 Apr 2013 08:39:55 +0200
Subject: [PATCH 005/134] 8011802: NPG: init_dependencies in class loader data
graph can cause invalid CLD
Restructure initialization of ClassLoaderData to not add a new instance if init_dependencies fail
Reviewed-by: stefank, coleenp
---
.../share/vm/classfile/classLoaderData.cpp | 43 +++++++++++--------
.../share/vm/classfile/classLoaderData.hpp | 2 +-
.../vm/classfile/classLoaderData.inline.hpp | 9 ++--
3 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index 75b9f34f2b2..083b896f9dc 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -53,6 +53,7 @@
#include "classfile/metadataOnStackMark.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
+#include "memory/gcLocker.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp"
@@ -423,7 +424,7 @@ void ClassLoaderData::free_deallocate_list() {
// These anonymous class loaders are to contain classes used for JSR292
ClassLoaderData* ClassLoaderData::anonymous_class_loader_data(oop loader, TRAPS) {
// Add a new class loader data to the graph.
- return ClassLoaderDataGraph::add(NULL, loader, CHECK_NULL);
+ return ClassLoaderDataGraph::add(loader, true, CHECK_NULL);
}
const char* ClassLoaderData::loader_name() {
@@ -495,30 +496,40 @@ ClassLoaderData* ClassLoaderDataGraph::_head = NULL;
ClassLoaderData* ClassLoaderDataGraph::_unloading = NULL;
ClassLoaderData* ClassLoaderDataGraph::_saved_head = NULL;
-
// Add a new class loader data node to the list. Assign the newly created
// ClassLoaderData into the java/lang/ClassLoader object as a hidden field
-ClassLoaderData* ClassLoaderDataGraph::add(ClassLoaderData** cld_addr, Handle loader, TRAPS) {
+ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool is_anonymous, TRAPS) {
// Not assigned a class loader data yet.
// Create one.
- ClassLoaderData* *list_head = &_head;
- ClassLoaderData* next = _head;
-
- bool is_anonymous = (cld_addr == NULL);
ClassLoaderData* cld = new ClassLoaderData(loader, is_anonymous);
+ cld->init_dependencies(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ delete cld;
+ return NULL;
+ }
- if (cld_addr != NULL) {
- // First, Atomically set it
- ClassLoaderData* old = (ClassLoaderData*) Atomic::cmpxchg_ptr(cld, cld_addr, NULL);
- if (old != NULL) {
- delete cld;
- // Returns the data.
- return old;
+ No_Safepoint_Verifier no_safepoints; // nothing is keeping the dependencies array in cld alive
+ // make sure we don't encounter a GC until we've inserted
+ // cld into the CLDG
+
+ if (!is_anonymous) {
+ ClassLoaderData** cld_addr = java_lang_ClassLoader::loader_data_addr(loader());
+ if (cld_addr != NULL) {
+ // First, Atomically set it
+ ClassLoaderData* old = (ClassLoaderData*) Atomic::cmpxchg_ptr(cld, cld_addr, NULL);
+ if (old != NULL) {
+ delete cld;
+ // Returns the data.
+ return old;
+ }
}
}
// We won the race, and therefore the task of adding the data to the list of
// class loader data
+ ClassLoaderData** list_head = &_head;
+ ClassLoaderData* next = _head;
+
do {
cld->set_next(next);
ClassLoaderData* exchanged = (ClassLoaderData*)Atomic::cmpxchg_ptr(cld, list_head, next);
@@ -531,10 +542,6 @@ ClassLoaderData* ClassLoaderDataGraph::add(ClassLoaderData** cld_addr, Handle lo
cld->loader_name());
tty->print_cr("]");
}
- // Create dependencies after the CLD is added to the list. Otherwise,
- // the GC GC will not find the CLD and the _class_loader field will
- // not be updated.
- cld->init_dependencies(CHECK_NULL);
return cld;
}
next = exchanged;
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp
index e6315182e18..e4e342280c3 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp
@@ -62,7 +62,7 @@ class ClassLoaderDataGraph : public AllStatic {
// CMS support.
static ClassLoaderData* _saved_head;
- static ClassLoaderData* add(ClassLoaderData** loader_data_addr, Handle class_loader, TRAPS);
+ static ClassLoaderData* add(Handle class_loader, bool anonymous, TRAPS);
public:
static ClassLoaderData* find_or_create(Handle class_loader, TRAPS);
static void purge();
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp b/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp
index b3a5ccf86d1..018b6761c50 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp
@@ -43,10 +43,9 @@ inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader, TRAP
assert(loader() != NULL,"Must be a class loader");
// Gets the class loader data out of the java/lang/ClassLoader object, if non-null
// it's already in the loader_data, so no need to add
- ClassLoaderData** loader_data_addr = java_lang_ClassLoader::loader_data_addr(loader());
- ClassLoaderData* loader_data_id = *loader_data_addr;
- if (loader_data_id) {
- return loader_data_id;
+ ClassLoaderData* loader_data= java_lang_ClassLoader::loader_data(loader());
+ if (loader_data) {
+ return loader_data;
}
- return ClassLoaderDataGraph::add(loader_data_addr, loader, THREAD);
+ return ClassLoaderDataGraph::add(loader, false, THREAD);
}
From eafc00bc250c1230969c73d436e564ddc102d97b Mon Sep 17 00:00:00 2001
From: John Cuthbertson
Date: Thu, 18 Apr 2013 10:09:23 -0700
Subject: [PATCH 006/134] 8011724: G1: Stack allocate instances of
HeapRegionRemSetIterator
Stack allocate instances of HeapRegionRemSetIterator during RSet scanning.
Reviewed-by: brutisso, jwilhelm
---
.../gc_implementation/g1/g1CollectedHeap.cpp | 7 ---
.../gc_implementation/g1/g1CollectedHeap.hpp | 12 -----
.../vm/gc_implementation/g1/g1RemSet.cpp | 5 +-
.../vm/gc_implementation/g1/g1RemSet.hpp | 12 ++---
.../gc_implementation/g1/heapRegionRemSet.cpp | 52 ++++++-------------
.../gc_implementation/g1/heapRegionRemSet.hpp | 17 +++---
.../vm/gc_implementation/g1/sparsePRT.cpp | 4 --
.../vm/gc_implementation/g1/sparsePRT.hpp | 21 +++-----
8 files changed, 38 insertions(+), 92 deletions(-)
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index 4c1b75133b8..ab0434e3c55 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -1955,13 +1955,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
int n_rem_sets = HeapRegionRemSet::num_par_rem_sets();
assert(n_rem_sets > 0, "Invariant.");
- HeapRegionRemSetIterator** iter_arr =
- NEW_C_HEAP_ARRAY(HeapRegionRemSetIterator*, n_queues, mtGC);
- for (int i = 0; i < n_queues; i++) {
- iter_arr[i] = new HeapRegionRemSetIterator();
- }
- _rem_set_iterator = iter_arr;
-
_worker_cset_start_region = NEW_C_HEAP_ARRAY(HeapRegion*, n_queues, mtGC);
_worker_cset_start_region_time_stamp = NEW_C_HEAP_ARRAY(unsigned int, n_queues, mtGC);
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
index 9080bc353cb..32f5a46b471 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
@@ -786,9 +786,6 @@ protected:
// concurrently after the collection.
DirtyCardQueueSet _dirty_card_queue_set;
- // The Heap Region Rem Set Iterator.
- HeapRegionRemSetIterator** _rem_set_iterator;
-
// The closure used to refine a single card.
RefineCardTableEntryClosure* _refine_cte_cl;
@@ -1113,15 +1110,6 @@ public:
G1RemSet* g1_rem_set() const { return _g1_rem_set; }
ModRefBarrierSet* mr_bs() const { return _mr_bs; }
- // The rem set iterator.
- HeapRegionRemSetIterator* rem_set_iterator(int i) {
- return _rem_set_iterator[i];
- }
-
- HeapRegionRemSetIterator* rem_set_iterator() {
- return _rem_set_iterator[0];
- }
-
unsigned get_gc_time_stamp() {
return _gc_time_stamp;
}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
index 04af52e9478..e7151071e9f 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
@@ -169,14 +169,13 @@ public:
// _try_claimed || r->claim_iter()
// is true: either we're supposed to work on claimed-but-not-complete
// regions, or we successfully claimed the region.
- HeapRegionRemSetIterator* iter = _g1h->rem_set_iterator(_worker_i);
- hrrs->init_iterator(iter);
+ HeapRegionRemSetIterator iter(hrrs);
size_t card_index;
// We claim cards in block so as to recude the contention. The block size is determined by
// the G1RSetScanBlockSize parameter.
size_t jump_to_card = hrrs->iter_claimed_next(_block_size);
- for (size_t current_card = 0; iter->has_next(card_index); current_card++) {
+ for (size_t current_card = 0; iter.has_next(card_index); current_card++) {
if (current_card >= jump_to_card + _block_size) {
jump_to_card = hrrs->iter_claimed_next(_block_size);
}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
index 0468e9ac312..eee6b447087 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
@@ -53,14 +53,14 @@ protected:
NumSeqTasks = 1
};
- CardTableModRefBS* _ct_bs;
- SubTasksDone* _seq_task;
- G1CollectorPolicy* _g1p;
+ CardTableModRefBS* _ct_bs;
+ SubTasksDone* _seq_task;
+ G1CollectorPolicy* _g1p;
- ConcurrentG1Refine* _cg1r;
+ ConcurrentG1Refine* _cg1r;
- size_t* _cards_scanned;
- size_t _total_cards_scanned;
+ size_t* _cards_scanned;
+ size_t _total_cards_scanned;
// Used for caching the closure that is responsible for scanning
// references into the collection set.
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
index 56e94051c3a..df215a5cd93 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
@@ -877,14 +877,9 @@ bool HeapRegionRemSet::iter_is_complete() {
return _iter_state == Complete;
}
-void HeapRegionRemSet::init_iterator(HeapRegionRemSetIterator* iter) const {
- iter->initialize(this);
-}
-
#ifndef PRODUCT
void HeapRegionRemSet::print() const {
- HeapRegionRemSetIterator iter;
- init_iterator(&iter);
+ HeapRegionRemSetIterator iter(this);
size_t card_index;
while (iter.has_next(card_index)) {
HeapWord* card_start =
@@ -928,35 +923,23 @@ void HeapRegionRemSet::scrub(CardTableModRefBS* ctbs,
//-------------------- Iteration --------------------
-HeapRegionRemSetIterator::
-HeapRegionRemSetIterator() :
- _hrrs(NULL),
+HeapRegionRemSetIterator:: HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs) :
+ _hrrs(hrrs),
_g1h(G1CollectedHeap::heap()),
- _bosa(NULL),
- _sparse_iter() { }
-
-void HeapRegionRemSetIterator::initialize(const HeapRegionRemSet* hrrs) {
- _hrrs = hrrs;
- _coarse_map = &_hrrs->_other_regions._coarse_map;
- _fine_grain_regions = _hrrs->_other_regions._fine_grain_regions;
- _bosa = _hrrs->bosa();
-
- _is = Sparse;
+ _coarse_map(&hrrs->_other_regions._coarse_map),
+ _fine_grain_regions(hrrs->_other_regions._fine_grain_regions),
+ _bosa(hrrs->bosa()),
+ _is(Sparse),
// Set these values so that we increment to the first region.
- _coarse_cur_region_index = -1;
- _coarse_cur_region_cur_card = (HeapRegion::CardsPerRegion-1);
-
- _cur_region_cur_card = 0;
-
- _fine_array_index = -1;
- _fine_cur_prt = NULL;
-
- _n_yielded_coarse = 0;
- _n_yielded_fine = 0;
- _n_yielded_sparse = 0;
-
- _sparse_iter.init(&hrrs->_other_regions._sparse_table);
-}
+ _coarse_cur_region_index(-1),
+ _coarse_cur_region_cur_card(HeapRegion::CardsPerRegion-1),
+ _cur_region_cur_card(0),
+ _fine_array_index(-1),
+ _fine_cur_prt(NULL),
+ _n_yielded_coarse(0),
+ _n_yielded_fine(0),
+ _n_yielded_sparse(0),
+ _sparse_iter(&hrrs->_other_regions._sparse_table) {}
bool HeapRegionRemSetIterator::coarse_has_next(size_t& card_index) {
if (_hrrs->_other_regions._n_coarse_entries == 0) return false;
@@ -1209,8 +1192,7 @@ void HeapRegionRemSet::test() {
hrrs->add_reference((OopOrNarrowOopStar)hr5->bottom());
// Now, does iteration yield these three?
- HeapRegionRemSetIterator iter;
- hrrs->init_iterator(&iter);
+ HeapRegionRemSetIterator iter(hrrs);
size_t sum = 0;
size_t card_index;
while (iter.has_next(card_index)) {
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
index 1b1d42d7a35..2e165074e10 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
@@ -281,9 +281,6 @@ public:
return (_iter_state == Unclaimed) && (_iter_claimed == 0);
}
- // Initialize the given iterator to iterate over this rem set.
- void init_iterator(HeapRegionRemSetIterator* iter) const;
-
// The actual # of bytes this hr_remset takes up.
size_t mem_size() {
return _other_regions.mem_size()
@@ -345,9 +342,9 @@ public:
#endif
};
-class HeapRegionRemSetIterator : public CHeapObj {
+class HeapRegionRemSetIterator : public StackObj {
- // The region over which we're iterating.
+ // The region RSet over which we're iterating.
const HeapRegionRemSet* _hrrs;
// Local caching of HRRS fields.
@@ -362,8 +359,10 @@ class HeapRegionRemSetIterator : public CHeapObj {
size_t _n_yielded_coarse;
size_t _n_yielded_sparse;
- // If true we're iterating over the coarse table; if false the fine
- // table.
+ // Indicates what granularity of table that we're currently iterating over.
+ // We start iterating over the sparse table, progress to the fine grain
+ // table, and then finish with the coarse table.
+ // See HeapRegionRemSetIterator::has_next().
enum IterState {
Sparse,
Fine,
@@ -403,9 +402,7 @@ class HeapRegionRemSetIterator : public CHeapObj {
public:
// We require an iterator to be initialized before use, so the
// constructor does little.
- HeapRegionRemSetIterator();
-
- void initialize(const HeapRegionRemSet* hrrs);
+ HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs);
// If there remains one or more cards to be yielded, returns true and
// sets "card_index" to one of those cards (which is then considered
diff --git a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
index 0daa63512a3..a08e8a9801e 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
@@ -35,10 +35,6 @@
#define UNROLL_CARD_LOOPS 1
-void SparsePRT::init_iterator(SparsePRTIter* sprt_iter) {
- sprt_iter->init(this);
-}
-
void SparsePRTEntry::init(RegionIdx_t region_ind) {
_region_ind = region_ind;
_next_index = NullEntry;
diff --git a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp
index ab0ab1f0205..6e821f73bbc 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp
@@ -192,18 +192,11 @@ class RSHashTableIter VALUE_OBJ_CLASS_SPEC {
size_t compute_card_ind(CardIdx_t ci);
public:
- RSHashTableIter() :
- _tbl_ind(RSHashTable::NullEntry),
+ RSHashTableIter(RSHashTable* rsht) :
+ _tbl_ind(RSHashTable::NullEntry), // So that first increment gets to 0.
_bl_ind(RSHashTable::NullEntry),
_card_ind((SparsePRTEntry::cards_num() - 1)),
- _rsht(NULL) {}
-
- void init(RSHashTable* rsht) {
- _rsht = rsht;
- _tbl_ind = -1; // So that first increment gets to 0.
- _bl_ind = RSHashTable::NullEntry;
- _card_ind = (SparsePRTEntry::cards_num() - 1);
- }
+ _rsht(rsht) {}
bool has_next(size_t& card_index);
};
@@ -284,8 +277,6 @@ public:
static void cleanup_all();
RSHashTable* cur() const { return _cur; }
- void init_iterator(SparsePRTIter* sprt_iter);
-
static void add_to_expanded_list(SparsePRT* sprt);
static SparsePRT* get_from_expanded_list();
@@ -321,9 +312,9 @@ public:
class SparsePRTIter: public RSHashTableIter {
public:
- void init(const SparsePRT* sprt) {
- RSHashTableIter::init(sprt->cur());
- }
+ SparsePRTIter(const SparsePRT* sprt) :
+ RSHashTableIter(sprt->cur()) {}
+
bool has_next(size_t& card_index) {
return RSHashTableIter::has_next(card_index);
}
From d72b5162011dee40ac4658942c0bbb9b01f9b15d Mon Sep 17 00:00:00 2001
From: Jon Masamitsu
Date: Tue, 12 Feb 2013 14:15:45 -0800
Subject: [PATCH 007/134] 8008966: NPG: Inefficient Metaspace counter functions
cause large young GC regressions
Reviewed-by: mgerdin, coleenp
---
.../share/vm/classfile/classLoaderData.cpp | 2 +
.../gc_implementation/g1/g1CollectedHeap.cpp | 3 +-
.../parallelScavenge/psMarkSweep.cpp | 3 +-
.../parallelScavenge/psParallelCompact.cpp | 3 +-
.../shared/vmGCOperations.cpp | 5 +-
hotspot/src/share/vm/memory/filemap.cpp | 4 +-
.../src/share/vm/memory/genCollectedHeap.cpp | 3 +-
hotspot/src/share/vm/memory/metaspace.cpp | 422 ++++++++++++------
hotspot/src/share/vm/memory/metaspace.hpp | 96 +++-
.../src/share/vm/memory/metaspaceCounters.cpp | 22 +-
.../src/share/vm/memory/metaspaceCounters.hpp | 1 +
.../src/share/vm/memory/metaspaceShared.cpp | 9 +-
12 files changed, 398 insertions(+), 175 deletions(-)
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index 083b896f9dc..c5486d458fa 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -672,6 +672,8 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure) {
dead->unload();
data = data->next();
// Remove from loader list.
+ // This class loader data will no longer be found
+ // in the ClassLoaderDataGraph.
if (prev != NULL) {
prev->set_next(data);
} else {
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index ab0434e3c55..4fee47a0021 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -1304,7 +1304,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
print_heap_before_gc();
- size_t metadata_prev_used = MetaspaceAux::used_in_bytes();
+ size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes();
HRSPhaseSetter x(HRSPhaseFullGC);
verify_region_sets_optional();
@@ -1425,6 +1425,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
// Delete metaspaces for unloaded class loaders and clean up loader_data graph
ClassLoaderDataGraph::purge();
+ MetaspaceAux::verify_metrics();
// Note: since we've just done a full GC, concurrent
// marking is no longer active. Therefore we need not
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
index cf07854cd9c..4df7be7f870 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
@@ -177,7 +177,7 @@ bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
size_t prev_used = heap->used();
// Capture metadata size before collection for sizing.
- size_t metadata_prev_used = MetaspaceAux::used_in_bytes();
+ size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes();
// For PrintGCDetails
size_t old_gen_prev_used = old_gen->used_in_bytes();
@@ -238,6 +238,7 @@ bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
// Delete metaspaces for unloaded class loaders and clean up loader_data graph
ClassLoaderDataGraph::purge();
+ MetaspaceAux::verify_metrics();
BiasedLocking::restore_marks();
Threads::gc_epilogue();
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
index d0d50a7f699..5bc000a4680 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
@@ -891,7 +891,7 @@ public:
_heap_used = heap->used();
_young_gen_used = heap->young_gen()->used_in_bytes();
_old_gen_used = heap->old_gen()->used_in_bytes();
- _metadata_used = MetaspaceAux::used_in_bytes();
+ _metadata_used = MetaspaceAux::allocated_used_bytes();
};
size_t heap_used() const { return _heap_used; }
@@ -1026,6 +1026,7 @@ void PSParallelCompact::post_compact()
// Delete metaspaces for unloaded class loaders and clean up loader_data graph
ClassLoaderDataGraph::purge();
+ MetaspaceAux::verify_metrics();
Threads::gc_epilogue();
CodeCache::gc_epilogue();
diff --git a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
index 756ed28f010..211a084ab38 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
@@ -225,7 +225,10 @@ void VM_CollectForMetadataAllocation::doit() {
gclog_or_tty->print_cr("\nCMS full GC for Metaspace");
}
heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold);
- _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
+ // After a GC try to allocate without expanding. Could fail
+ // and expansion will be tried below.
+ _result =
+ _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
}
if (_result == NULL && !UseConcMarkSweepGC /* CMS already tried */) {
// If still failing, allow the Metaspace to expand.
diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp
index 133685932fd..dbc0c87edce 100644
--- a/hotspot/src/share/vm/memory/filemap.cpp
+++ b/hotspot/src/share/vm/memory/filemap.cpp
@@ -238,8 +238,8 @@ void FileMapInfo::write_header() {
void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) {
align_file_position();
- size_t used = space->used_words(Metaspace::NonClassType) * BytesPerWord;
- size_t capacity = space->capacity_words(Metaspace::NonClassType) * BytesPerWord;
+ size_t used = space->used_bytes_slow(Metaspace::NonClassType);
+ size_t capacity = space->capacity_bytes_slow(Metaspace::NonClassType);
struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i];
write_region(i, (char*)space->bottom(), used, capacity, read_only, false);
}
diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
index a04eb3cb721..370dd500d84 100644
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
@@ -377,7 +377,7 @@ void GenCollectedHeap::do_collection(bool full,
ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());
- const size_t metadata_prev_used = MetaspaceAux::used_in_bytes();
+ const size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes();
print_heap_before_gc();
@@ -556,6 +556,7 @@ void GenCollectedHeap::do_collection(bool full,
if (complete) {
// Delete metaspaces for unloaded class loaders and clean up loader_data graph
ClassLoaderDataGraph::purge();
+ MetaspaceAux::verify_metrics();
// Resize the metaspace capacity after full collections
MetaspaceGC::compute_new_size();
update_full_collections_completed();
diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
index 3cc6d8d4959..e94750eb642 100644
--- a/hotspot/src/share/vm/memory/metaspace.cpp
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
@@ -47,7 +47,6 @@ typedef BinaryTreeDictionary ChunkTreeDictionary;
// the free chunk lists
const bool metaspace_slow_verify = false;
-
// Parameters for stress mode testing
const uint metadata_deallocate_a_lot_block = 10;
const uint metadata_deallocate_a_lock_chunk = 3;
@@ -220,7 +219,6 @@ class ChunkManager VALUE_OBJ_CLASS_SPEC {
void print_on(outputStream* st);
};
-
// Used to manage the free list of Metablocks (a block corresponds
// to the allocation of a quantum of metadata).
class BlockFreelist VALUE_OBJ_CLASS_SPEC {
@@ -298,7 +296,7 @@ class VirtualSpaceNode : public CHeapObj {
MemRegion* reserved() { return &_reserved; }
VirtualSpace* virtual_space() const { return (VirtualSpace*) &_virtual_space; }
- // Returns true if "word_size" is available in the virtual space
+ // Returns true if "word_size" is available in the VirtualSpace
bool is_available(size_t word_size) { return _top + word_size <= end(); }
MetaWord* top() const { return _top; }
@@ -313,6 +311,7 @@ class VirtualSpaceNode : public CHeapObj {
// used and capacity in this single entry in the list
size_t used_words_in_vs() const;
size_t capacity_words_in_vs() const;
+ size_t free_words_in_vs() const;
bool initialize();
@@ -449,6 +448,8 @@ class VirtualSpaceList : public CHeapObj {
VirtualSpaceList(size_t word_size);
VirtualSpaceList(ReservedSpace rs);
+ size_t free_bytes();
+
Metachunk* get_new_chunk(size_t word_size,
size_t grow_chunks_by_words,
size_t medium_chunk_bunch);
@@ -579,7 +580,11 @@ class SpaceManager : public CHeapObj {
bool has_small_chunk_limit() { return !vs_list()->is_class(); }
// Sum of all space in allocated chunks
- size_t _allocation_total;
+ size_t _allocated_blocks_words;
+
+ // Sum of all allocated chunks
+ size_t _allocated_chunks_words;
+ size_t _allocated_chunks_count;
// Free lists of blocks are per SpaceManager since they
// are assumed to be in chunks in use by the SpaceManager
@@ -635,12 +640,27 @@ class SpaceManager : public CHeapObj {
size_t medium_chunk_size() { return (size_t) vs_list()->is_class() ? ClassMediumChunk : MediumChunk; }
size_t medium_chunk_bunch() { return medium_chunk_size() * MediumChunkMultiple; }
- size_t allocation_total() const { return _allocation_total; }
- void inc_allocation_total(size_t v) { Atomic::add_ptr(v, &_allocation_total); }
+ size_t allocated_blocks_words() const { return _allocated_blocks_words; }
+ size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; }
+ size_t allocated_chunks_words() const { return _allocated_chunks_words; }
+ size_t allocated_chunks_count() const { return _allocated_chunks_count; }
+
bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); }
static Mutex* expand_lock() { return _expand_lock; }
+ // Increment the per Metaspace and global running sums for Metachunks
+ // by the given size. This is used when a Metachunk to added to
+ // the in-use list.
+ void inc_size_metrics(size_t words);
+ // Increment the per Metaspace and global running sums Metablocks by the given
+ // size. This is used when a Metablock is allocated.
+ void inc_used_metrics(size_t words);
+ // Delete the portion of the running sums for this SpaceManager. That is,
+ // the globals running sums for the Metachunks and Metablocks are
+ // decremented for all the Metachunks in-use by this SpaceManager.
+ void dec_total_from_size_metrics();
+
// Set the sizes for the initial chunks.
void get_initial_chunk_sizes(Metaspace::MetaspaceType type,
size_t* chunk_word_size,
@@ -686,7 +706,7 @@ class SpaceManager : public CHeapObj {
void verify_chunk_size(Metachunk* chunk);
NOT_PRODUCT(void mangle_freed_chunks();)
#ifdef ASSERT
- void verify_allocation_total();
+ void verify_allocated_blocks_words();
#endif
};
@@ -797,6 +817,9 @@ size_t VirtualSpaceNode::capacity_words_in_vs() const {
return pointer_delta(end(), bottom(), sizeof(MetaWord));
}
+size_t VirtualSpaceNode::free_words_in_vs() const {
+ return pointer_delta(end(), top(), sizeof(MetaWord));
+}
// Allocates the chunk from the virtual space only.
// This interface is also used internally for debugging. Not all
@@ -1071,6 +1094,10 @@ VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) :
link_vs(class_entry, rs.size()/BytesPerWord);
}
+size_t VirtualSpaceList::free_bytes() {
+ return virtual_space_list()->free_words_in_vs() * BytesPerWord;
+}
+
// Allocate another meta virtual space and add it to the list.
bool VirtualSpaceList::grow_vs(size_t vs_word_size) {
assert_lock_strong(SpaceManager::expand_lock());
@@ -1211,9 +1238,9 @@ bool VirtualSpaceList::contains(const void *ptr) {
//
// After the GC the compute_new_size() for MetaspaceGC is called to
// resize the capacity of the metaspaces. The current implementation
-// is based on the flags MinMetaspaceFreeRatio and MaxHeapFreeRatio used
+// is based on the flags MinMetaspaceFreeRatio and MaxMetaspaceFreeRatio used
// to resize the Java heap by some GC's. New flags can be implemented
-// if really needed. MinHeapFreeRatio is used to calculate how much
+// if really needed. MinMetaspaceFreeRatio is used to calculate how much
// free space is desirable in the metaspace capacity to decide how much
// to increase the HWM. MaxMetaspaceFreeRatio is used to decide how much
// free space is desirable in the metaspace capacity before decreasing
@@ -1248,7 +1275,11 @@ size_t MetaspaceGC::delta_capacity_until_GC(size_t word_size) {
}
bool MetaspaceGC::should_expand(VirtualSpaceList* vsl, size_t word_size) {
+
+ size_t committed_capacity_bytes = MetaspaceAux::allocated_capacity_bytes();
// If the user wants a limit, impose one.
+ size_t max_metaspace_size_bytes = MaxMetaspaceSize;
+ size_t metaspace_size_bytes = MetaspaceSize;
if (!FLAG_IS_DEFAULT(MaxMetaspaceSize) &&
MetaspaceAux::reserved_in_bytes() >= MaxMetaspaceSize) {
return false;
@@ -1260,57 +1291,48 @@ bool MetaspaceGC::should_expand(VirtualSpaceList* vsl, size_t word_size) {
// If this is part of an allocation after a GC, expand
// unconditionally.
- if(MetaspaceGC::expand_after_GC()) {
+ if (MetaspaceGC::expand_after_GC()) {
return true;
}
- size_t metaspace_size_words = MetaspaceSize / BytesPerWord;
+
// If the capacity is below the minimum capacity, allow the
// expansion. Also set the high-water-mark (capacity_until_GC)
// to that minimum capacity so that a GC will not be induced
// until that minimum capacity is exceeded.
- if (vsl->capacity_words_sum() < metaspace_size_words ||
+ if (committed_capacity_bytes < metaspace_size_bytes ||
capacity_until_GC() == 0) {
- set_capacity_until_GC(metaspace_size_words);
+ set_capacity_until_GC(metaspace_size_bytes);
return true;
} else {
- if (vsl->capacity_words_sum() < capacity_until_GC()) {
+ if (committed_capacity_bytes < capacity_until_GC()) {
return true;
} else {
if (TraceMetadataChunkAllocation && Verbose) {
gclog_or_tty->print_cr(" allocation request size " SIZE_FORMAT
" capacity_until_GC " SIZE_FORMAT
- " capacity_words_sum " SIZE_FORMAT
- " used_words_sum " SIZE_FORMAT
- " free chunks " SIZE_FORMAT
- " free chunks count %d",
+ " allocated_capacity_bytes " SIZE_FORMAT,
word_size,
capacity_until_GC(),
- vsl->capacity_words_sum(),
- vsl->used_words_sum(),
- vsl->chunk_manager()->free_chunks_total(),
- vsl->chunk_manager()->free_chunks_count());
+ MetaspaceAux::allocated_capacity_bytes());
}
return false;
}
}
}
-// Variables are in bytes
+
void MetaspaceGC::compute_new_size() {
assert(_shrink_factor <= 100, "invalid shrink factor");
uint current_shrink_factor = _shrink_factor;
_shrink_factor = 0;
- VirtualSpaceList *vsl = Metaspace::space_list();
-
- size_t capacity_after_gc = vsl->capacity_bytes_sum();
- // Check to see if these two can be calculated without walking the CLDG
- size_t used_after_gc = vsl->used_bytes_sum();
- size_t capacity_until_GC = vsl->capacity_bytes_sum();
- size_t free_after_gc = capacity_until_GC - used_after_gc;
+ // Until a faster way of calculating the "used" quantity is implemented,
+ // use "capacity".
+ const size_t used_after_gc = MetaspaceAux::allocated_capacity_bytes();
+ const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC();
const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0;
const double maximum_used_percentage = 1.0 - minimum_free_percentage;
@@ -1323,45 +1345,34 @@ void MetaspaceGC::compute_new_size() {
MetaspaceSize);
if (PrintGCDetails && Verbose) {
- const double free_percentage = ((double)free_after_gc) / capacity_until_GC;
gclog_or_tty->print_cr("\nMetaspaceGC::compute_new_size: ");
gclog_or_tty->print_cr(" "
" minimum_free_percentage: %6.2f"
" maximum_used_percentage: %6.2f",
minimum_free_percentage,
maximum_used_percentage);
- double d_free_after_gc = free_after_gc / (double) K;
gclog_or_tty->print_cr(" "
- " free_after_gc : %6.1fK"
- " used_after_gc : %6.1fK"
- " capacity_after_gc : %6.1fK"
- " metaspace HWM : %6.1fK",
- free_after_gc / (double) K,
- used_after_gc / (double) K,
- capacity_after_gc / (double) K,
- capacity_until_GC / (double) K);
- gclog_or_tty->print_cr(" "
- " free_percentage: %6.2f",
- free_percentage);
+ " used_after_gc : %6.1fKB",
+ used_after_gc / (double) K);
}
+ size_t shrink_bytes = 0;
if (capacity_until_GC < minimum_desired_capacity) {
// If we have less capacity below the metaspace HWM, then
// increment the HWM.
size_t expand_bytes = minimum_desired_capacity - capacity_until_GC;
// Don't expand unless it's significant
if (expand_bytes >= MinMetaspaceExpansion) {
- size_t expand_words = expand_bytes / BytesPerWord;
- MetaspaceGC::inc_capacity_until_GC(expand_words);
+ MetaspaceGC::set_capacity_until_GC(capacity_until_GC + expand_bytes);
}
if (PrintGCDetails && Verbose) {
- size_t new_capacity_until_GC = MetaspaceGC::capacity_until_GC_in_bytes();
+ size_t new_capacity_until_GC = capacity_until_GC;
gclog_or_tty->print_cr(" expanding:"
- " minimum_desired_capacity: %6.1fK"
- " expand_words: %6.1fK"
- " MinMetaspaceExpansion: %6.1fK"
- " new metaspace HWM: %6.1fK",
+ " minimum_desired_capacity: %6.1fKB"
+ " expand_bytes: %6.1fKB"
+ " MinMetaspaceExpansion: %6.1fKB"
+ " new metaspace HWM: %6.1fKB",
minimum_desired_capacity / (double) K,
expand_bytes / (double) K,
MinMetaspaceExpansion / (double) K,
@@ -1371,11 +1382,10 @@ void MetaspaceGC::compute_new_size() {
}
// No expansion, now see if we want to shrink
- size_t shrink_words = 0;
// We would never want to shrink more than this
- size_t max_shrink_words = capacity_until_GC - minimum_desired_capacity;
- assert(max_shrink_words >= 0, err_msg("max_shrink_words " SIZE_FORMAT,
- max_shrink_words));
+ size_t max_shrink_bytes = capacity_until_GC - minimum_desired_capacity;
+ assert(max_shrink_bytes >= 0, err_msg("max_shrink_bytes " SIZE_FORMAT,
+ max_shrink_bytes));
// Should shrinking be considered?
if (MaxMetaspaceFreeRatio < 100) {
@@ -1385,17 +1395,15 @@ void MetaspaceGC::compute_new_size() {
size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx));
maximum_desired_capacity = MAX2(maximum_desired_capacity,
MetaspaceSize);
- if (PrintGC && Verbose) {
+ if (PrintGCDetails && Verbose) {
gclog_or_tty->print_cr(" "
" maximum_free_percentage: %6.2f"
" minimum_used_percentage: %6.2f",
maximum_free_percentage,
minimum_used_percentage);
gclog_or_tty->print_cr(" "
- " capacity_until_GC: %6.1fK"
- " minimum_desired_capacity: %6.1fK"
- " maximum_desired_capacity: %6.1fK",
- capacity_until_GC / (double) K,
+ " minimum_desired_capacity: %6.1fKB"
+ " maximum_desired_capacity: %6.1fKB",
minimum_desired_capacity / (double) K,
maximum_desired_capacity / (double) K);
}
@@ -1405,17 +1413,17 @@ void MetaspaceGC::compute_new_size() {
if (capacity_until_GC > maximum_desired_capacity) {
// Capacity too large, compute shrinking size
- shrink_words = capacity_until_GC - maximum_desired_capacity;
+ shrink_bytes = capacity_until_GC - maximum_desired_capacity;
// We don't want shrink all the way back to initSize if people call
// System.gc(), because some programs do that between "phases" and then
// we'd just have to grow the heap up again for the next phase. So we
// damp the shrinking: 0% on the first call, 10% on the second call, 40%
// on the third call, and 100% by the fourth call. But if we recompute
// size without shrinking, it goes back to 0%.
- shrink_words = shrink_words / 100 * current_shrink_factor;
- assert(shrink_words <= max_shrink_words,
+ shrink_bytes = shrink_bytes / 100 * current_shrink_factor;
+ assert(shrink_bytes <= max_shrink_bytes,
err_msg("invalid shrink size " SIZE_FORMAT " not <= " SIZE_FORMAT,
- shrink_words, max_shrink_words));
+ shrink_bytes, max_shrink_bytes));
if (current_shrink_factor == 0) {
_shrink_factor = 10;
} else {
@@ -1429,11 +1437,11 @@ void MetaspaceGC::compute_new_size() {
MetaspaceSize / (double) K,
maximum_desired_capacity / (double) K);
gclog_or_tty->print_cr(" "
- " shrink_words: %.1fK"
+ " shrink_bytes: %.1fK"
" current_shrink_factor: %d"
" new shrink factor: %d"
" MinMetaspaceExpansion: %.1fK",
- shrink_words / (double) K,
+ shrink_bytes / (double) K,
current_shrink_factor,
_shrink_factor,
MinMetaspaceExpansion / (double) K);
@@ -1441,23 +1449,11 @@ void MetaspaceGC::compute_new_size() {
}
}
-
// Don't shrink unless it's significant
- if (shrink_words >= MinMetaspaceExpansion) {
- VirtualSpaceNode* csp = vsl->current_virtual_space();
- size_t available_to_shrink = csp->capacity_words_in_vs() -
- csp->used_words_in_vs();
- shrink_words = MIN2(shrink_words, available_to_shrink);
- csp->shrink_by(shrink_words);
- MetaspaceGC::dec_capacity_until_GC(shrink_words);
- if (PrintGCDetails && Verbose) {
- size_t new_capacity_until_GC = MetaspaceGC::capacity_until_GC_in_bytes();
- gclog_or_tty->print_cr(" metaspace HWM: %.1fK", new_capacity_until_GC / (double) K);
- }
+ if (shrink_bytes >= MinMetaspaceExpansion &&
+ ((capacity_until_GC - shrink_bytes) >= MetaspaceSize)) {
+ MetaspaceGC::set_capacity_until_GC(capacity_until_GC - shrink_bytes);
}
- assert(used_after_gc <= vsl->capacity_bytes_sum(),
- "sanity check");
-
}
// Metadebug methods
@@ -1860,18 +1856,28 @@ size_t SpaceManager::sum_waste_in_chunks_in_use(ChunkIndex index) const {
}
size_t SpaceManager::sum_capacity_in_chunks_in_use() const {
- MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
- size_t sum = 0;
- for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
- Metachunk* chunk = chunks_in_use(i);
- while (chunk != NULL) {
- // Just changed this sum += chunk->capacity_word_size();
- // sum += chunk->word_size() - Metachunk::overhead();
- sum += chunk->capacity_word_size();
- chunk = chunk->next();
+ // For CMS use "allocated_chunks_words()" which does not need the
+ // Metaspace lock. For the other collectors sum over the
+ // lists. Use both methods as a check that "allocated_chunks_words()"
+ // is correct. That is, sum_capacity_in_chunks() is too expensive
+ // to use in the product and allocated_chunks_words() should be used
+ // but allow for checking that allocated_chunks_words() returns the same
+ // value as sum_capacity_in_chunks_in_use() which is the definitive
+ // answer.
+ if (UseConcMarkSweepGC) {
+ return allocated_chunks_words();
+ } else {
+ MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
+ size_t sum = 0;
+ for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+ Metachunk* chunk = chunks_in_use(i);
+ while (chunk != NULL) {
+ sum += chunk->capacity_word_size();
+ chunk = chunk->next();
+ }
}
- }
return sum;
+ }
}
size_t SpaceManager::sum_count_in_chunks_in_use() {
@@ -2029,12 +2035,44 @@ void SpaceManager::print_on(outputStream* st) const {
SpaceManager::SpaceManager(Mutex* lock,
VirtualSpaceList* vs_list) :
_vs_list(vs_list),
- _allocation_total(0),
+ _allocated_blocks_words(0),
+ _allocated_chunks_words(0),
+ _allocated_chunks_count(0),
_lock(lock)
{
initialize();
}
+void SpaceManager::inc_size_metrics(size_t words) {
+ assert_lock_strong(SpaceManager::expand_lock());
+ // Total of allocated Metachunks and allocated Metachunks count
+ // for each SpaceManager
+ _allocated_chunks_words = _allocated_chunks_words + words;
+ _allocated_chunks_count++;
+ // Global total of capacity in allocated Metachunks
+ MetaspaceAux::inc_capacity(words);
+ // Global total of allocated Metablocks.
+ // used_words_slow() includes the overhead in each
+ // Metachunk so include it in the used when the
+ // Metachunk is first added (so only added once per
+ // Metachunk).
+ MetaspaceAux::inc_used(Metachunk::overhead());
+}
+
+void SpaceManager::inc_used_metrics(size_t words) {
+ // Add to the per SpaceManager total
+ Atomic::add_ptr(words, &_allocated_blocks_words);
+ // Add to the global total
+ MetaspaceAux::inc_used(words);
+}
+
+void SpaceManager::dec_total_from_size_metrics() {
+ MetaspaceAux::dec_capacity(allocated_chunks_words());
+ MetaspaceAux::dec_used(allocated_blocks_words());
+ // Also deduct the overhead per Metachunk
+ MetaspaceAux::dec_used(allocated_chunks_count() * Metachunk::overhead());
+}
+
void SpaceManager::initialize() {
Metadebug::init_allocation_fail_alot_count();
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
@@ -2073,7 +2111,10 @@ void ChunkManager::return_chunks(ChunkIndex index, Metachunk* chunks) {
SpaceManager::~SpaceManager() {
// This call this->_lock which can't be done while holding expand_lock()
- const size_t in_use_before = sum_capacity_in_chunks_in_use();
+ assert(sum_capacity_in_chunks_in_use() == allocated_chunks_words(),
+ err_msg("sum_capacity_in_chunks_in_use() " SIZE_FORMAT
+ " allocated_chunks_words() " SIZE_FORMAT,
+ sum_capacity_in_chunks_in_use(), allocated_chunks_words()));
MutexLockerEx fcl(SpaceManager::expand_lock(),
Mutex::_no_safepoint_check_flag);
@@ -2082,6 +2123,8 @@ SpaceManager::~SpaceManager() {
chunk_manager->slow_locked_verify();
+ dec_total_from_size_metrics();
+
if (TraceMetadataChunkAllocation && Verbose) {
gclog_or_tty->print_cr("~SpaceManager(): " PTR_FORMAT, this);
locked_print_chunks_in_use_on(gclog_or_tty);
@@ -2092,7 +2135,7 @@ SpaceManager::~SpaceManager() {
// Have to update before the chunks_in_use lists are emptied
// below.
- chunk_manager->inc_free_chunks_total(in_use_before,
+ chunk_manager->inc_free_chunks_total(allocated_chunks_words(),
sum_count_in_chunks_in_use());
// Add all the chunks in use by this space manager
@@ -2158,7 +2201,6 @@ SpaceManager::~SpaceManager() {
chunk_manager->humongous_dictionary()->total_count(),
chunk_size_name(HumongousIndex));
}
- set_chunks_in_use(HumongousIndex, NULL);
chunk_manager->slow_locked_verify();
}
@@ -2238,12 +2280,17 @@ void SpaceManager::add_chunk(Metachunk* new_chunk, bool make_current) {
assert(new_chunk->word_size() > medium_chunk_size(), "List inconsistency");
}
+ // Add to the running sum of capacity
+ inc_size_metrics(new_chunk->word_size());
+
assert(new_chunk->is_empty(), "Not ready for reuse");
if (TraceMetadataChunkAllocation && Verbose) {
gclog_or_tty->print("SpaceManager::add_chunk: %d) ",
sum_count_in_chunks_in_use());
new_chunk->print_on(gclog_or_tty);
- vs_list()->chunk_manager()->locked_print_free_chunks(tty);
+ if (vs_list() != NULL) {
+ vs_list()->chunk_manager()->locked_print_free_chunks(tty);
+ }
}
}
@@ -2314,7 +2361,7 @@ MetaWord* SpaceManager::allocate_work(size_t word_size) {
// of memory if this returns null.
if (DumpSharedSpaces) {
assert(current_chunk() != NULL, "should never happen");
- inc_allocation_total(word_size);
+ inc_used_metrics(word_size);
return current_chunk()->allocate(word_size); // caller handles null result
}
if (current_chunk() != NULL) {
@@ -2325,7 +2372,7 @@ MetaWord* SpaceManager::allocate_work(size_t word_size) {
result = grow_and_allocate(word_size);
}
if (result > 0) {
- inc_allocation_total(word_size);
+ inc_used_metrics(word_size);
assert(result != (MetaWord*) chunks_in_use(MediumIndex),
"Head of the list is being allocated");
}
@@ -2359,20 +2406,14 @@ void SpaceManager::verify_chunk_size(Metachunk* chunk) {
}
#ifdef ASSERT
-void SpaceManager::verify_allocation_total() {
+void SpaceManager::verify_allocated_blocks_words() {
// Verification is only guaranteed at a safepoint.
- if (SafepointSynchronize::is_at_safepoint()) {
- gclog_or_tty->print_cr("Chunk " PTR_FORMAT " allocation_total " SIZE_FORMAT
- " sum_used_in_chunks_in_use " SIZE_FORMAT,
- this,
- allocation_total(),
- sum_used_in_chunks_in_use());
- }
- MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
- assert(allocation_total() == sum_used_in_chunks_in_use(),
+ assert(SafepointSynchronize::is_at_safepoint() || !Universe::is_fully_initialized(),
+ "Verification can fail if the applications is running");
+ assert(allocated_blocks_words() == sum_used_in_chunks_in_use(),
err_msg("allocation total is not consistent " SIZE_FORMAT
" vs " SIZE_FORMAT,
- allocation_total(), sum_used_in_chunks_in_use()));
+ allocated_blocks_words(), sum_used_in_chunks_in_use()));
}
#endif
@@ -2428,14 +2469,65 @@ void SpaceManager::mangle_freed_chunks() {
// MetaspaceAux
-size_t MetaspaceAux::used_in_bytes(Metaspace::MetadataType mdtype) {
+
+size_t MetaspaceAux::_allocated_capacity_words = 0;
+size_t MetaspaceAux::_allocated_used_words = 0;
+
+size_t MetaspaceAux::free_bytes() {
+ size_t result = 0;
+ if (Metaspace::class_space_list() != NULL) {
+ result = result + Metaspace::class_space_list()->free_bytes();
+ }
+ if (Metaspace::space_list() != NULL) {
+ result = result + Metaspace::space_list()->free_bytes();
+ }
+ return result;
+}
+
+void MetaspaceAux::dec_capacity(size_t words) {
+ assert_lock_strong(SpaceManager::expand_lock());
+ assert(words <= _allocated_capacity_words,
+ err_msg("About to decrement below 0: words " SIZE_FORMAT
+ " is greater than _allocated_capacity_words " SIZE_FORMAT,
+ words, _allocated_capacity_words));
+ _allocated_capacity_words = _allocated_capacity_words - words;
+}
+
+void MetaspaceAux::inc_capacity(size_t words) {
+ assert_lock_strong(SpaceManager::expand_lock());
+ // Needs to be atomic
+ _allocated_capacity_words = _allocated_capacity_words + words;
+}
+
+void MetaspaceAux::dec_used(size_t words) {
+ assert(words <= _allocated_used_words,
+ err_msg("About to decrement below 0: words " SIZE_FORMAT
+ " is greater than _allocated_used_words " SIZE_FORMAT,
+ words, _allocated_used_words));
+ // For CMS deallocation of the Metaspaces occurs during the
+ // sweep which is a concurrent phase. Protection by the expand_lock()
+ // is not enough since allocation is on a per Metaspace basis
+ // and protected by the Metaspace lock.
+ jlong minus_words = (jlong) - (jlong) words;
+ Atomic::add_ptr(minus_words, &_allocated_used_words);
+}
+
+void MetaspaceAux::inc_used(size_t words) {
+ // _allocated_used_words tracks allocations for
+ // each piece of metadata. Those allocations are
+ // generally done concurrently by different application
+ // threads so must be done atomically.
+ Atomic::add_ptr(words, &_allocated_used_words);
+}
+
+size_t MetaspaceAux::used_bytes_slow(Metaspace::MetadataType mdtype) {
size_t used = 0;
ClassLoaderDataGraphMetaspaceIterator iter;
while (iter.repeat()) {
Metaspace* msp = iter.get_next();
- // Sum allocation_total for each metaspace
+ // Sum allocated_blocks_words for each metaspace
if (msp != NULL) {
- used += msp->used_words(mdtype);
+ used += msp->used_words_slow(mdtype);
}
}
return used * BytesPerWord;
@@ -2453,13 +2545,15 @@ size_t MetaspaceAux::free_in_bytes(Metaspace::MetadataType mdtype) {
return free * BytesPerWord;
}
-size_t MetaspaceAux::capacity_in_bytes(Metaspace::MetadataType mdtype) {
- size_t capacity = free_chunks_total(mdtype);
+size_t MetaspaceAux::capacity_bytes_slow(Metaspace::MetadataType mdtype) {
+ // Don't count the space in the freelists. That space will be
+ // added to the capacity calculation as needed.
+ size_t capacity = 0;
ClassLoaderDataGraphMetaspaceIterator iter;
while (iter.repeat()) {
Metaspace* msp = iter.get_next();
if (msp != NULL) {
- capacity += msp->capacity_words(mdtype);
+ capacity += msp->capacity_words_slow(mdtype);
}
}
return capacity * BytesPerWord;
@@ -2486,23 +2580,30 @@ size_t MetaspaceAux::free_chunks_total_in_bytes(Metaspace::MetadataType mdtype)
return free_chunks_total(mdtype) * BytesPerWord;
}
+size_t MetaspaceAux::free_chunks_total() {
+ return free_chunks_total(Metaspace::ClassType) +
+ free_chunks_total(Metaspace::NonClassType);
+}
+
+size_t MetaspaceAux::free_chunks_total_in_bytes() {
+ return free_chunks_total() * BytesPerWord;
+}
+
void MetaspaceAux::print_metaspace_change(size_t prev_metadata_used) {
gclog_or_tty->print(", [Metaspace:");
if (PrintGCDetails && Verbose) {
gclog_or_tty->print(" " SIZE_FORMAT
"->" SIZE_FORMAT
- "(" SIZE_FORMAT "/" SIZE_FORMAT ")",
+ "(" SIZE_FORMAT ")",
prev_metadata_used,
- used_in_bytes(),
- capacity_in_bytes(),
+ allocated_capacity_bytes(),
reserved_in_bytes());
} else {
gclog_or_tty->print(" " SIZE_FORMAT "K"
"->" SIZE_FORMAT "K"
- "(" SIZE_FORMAT "K/" SIZE_FORMAT "K)",
+ "(" SIZE_FORMAT "K)",
prev_metadata_used / K,
- used_in_bytes()/ K,
- capacity_in_bytes()/K,
+ allocated_capacity_bytes() / K,
reserved_in_bytes()/ K);
}
@@ -2517,23 +2618,30 @@ void MetaspaceAux::print_on(outputStream* out) {
out->print_cr(" Metaspace total "
SIZE_FORMAT "K, used " SIZE_FORMAT "K,"
" reserved " SIZE_FORMAT "K",
- capacity_in_bytes()/K, used_in_bytes()/K, reserved_in_bytes()/K);
- out->print_cr(" data space "
- SIZE_FORMAT "K, used " SIZE_FORMAT "K,"
- " reserved " SIZE_FORMAT "K",
- capacity_in_bytes(nct)/K, used_in_bytes(nct)/K, reserved_in_bytes(nct)/K);
- out->print_cr(" class space "
- SIZE_FORMAT "K, used " SIZE_FORMAT "K,"
- " reserved " SIZE_FORMAT "K",
- capacity_in_bytes(ct)/K, used_in_bytes(ct)/K, reserved_in_bytes(ct)/K);
+ allocated_capacity_bytes()/K, allocated_used_bytes()/K, reserved_in_bytes()/K);
+#if 0
+// The calls to capacity_bytes_slow() and used_bytes_slow() cause
+// lock ordering assertion failures with some collectors. Do
+// not include this code until the lock ordering is fixed.
+ if (PrintGCDetails && Verbose) {
+ out->print_cr(" data space "
+ SIZE_FORMAT "K, used " SIZE_FORMAT "K,"
+ " reserved " SIZE_FORMAT "K",
+ capacity_bytes_slow(nct)/K, used_bytes_slow(nct)/K, reserved_in_bytes(nct)/K);
+ out->print_cr(" class space "
+ SIZE_FORMAT "K, used " SIZE_FORMAT "K,"
+ " reserved " SIZE_FORMAT "K",
+ capacity_bytes_slow(ct)/K, used_bytes_slow(ct)/K, reserved_in_bytes(ct)/K);
+ }
+#endif
}
// Print information for class space and data space separately.
// This is almost the same as above.
void MetaspaceAux::print_on(outputStream* out, Metaspace::MetadataType mdtype) {
size_t free_chunks_capacity_bytes = free_chunks_total_in_bytes(mdtype);
- size_t capacity_bytes = capacity_in_bytes(mdtype);
- size_t used_bytes = used_in_bytes(mdtype);
+ size_t capacity_bytes = capacity_bytes_slow(mdtype);
+ size_t used_bytes = used_bytes_slow(mdtype);
size_t free_bytes = free_in_bytes(mdtype);
size_t used_and_free = used_bytes + free_bytes +
free_chunks_capacity_bytes;
@@ -2606,6 +2714,36 @@ void MetaspaceAux::verify_free_chunks() {
Metaspace::class_space_list()->chunk_manager()->verify();
}
+void MetaspaceAux::verify_capacity() {
+#ifdef ASSERT
+ size_t running_sum_capacity_bytes = allocated_capacity_bytes();
+ // For purposes of the running sum of used, verify against capacity
+ size_t capacity_in_use_bytes = capacity_bytes_slow();
+ assert(running_sum_capacity_bytes == capacity_in_use_bytes,
+ err_msg("allocated_capacity_words() * BytesPerWord " SIZE_FORMAT
+ " capacity_bytes_slow()" SIZE_FORMAT,
+ running_sum_capacity_bytes, capacity_in_use_bytes));
+#endif
+}
+
+void MetaspaceAux::verify_used() {
+#ifdef ASSERT
+ size_t running_sum_used_bytes = allocated_used_bytes();
+ // For purposes of the running sum of used, verify against capacity
+ size_t used_in_use_bytes = used_bytes_slow();
+ assert(allocated_used_bytes() == used_in_use_bytes,
+ err_msg("allocated_used_bytes() " SIZE_FORMAT
+ " used_bytes_slow()()" SIZE_FORMAT,
+ allocated_used_bytes(), used_in_use_bytes));
+#endif
+}
+
+void MetaspaceAux::verify_metrics() {
+ verify_capacity();
+ verify_used();
+}
+
+
// Metaspace methods
size_t Metaspace::_first_chunk_word_size = 0;
@@ -2755,8 +2893,8 @@ MetaWord* Metaspace::expand_and_allocate(size_t word_size, MetadataType mdtype)
MetaWord* result;
MetaspaceGC::set_expand_after_GC(true);
size_t before_inc = MetaspaceGC::capacity_until_GC();
- size_t delta_words = MetaspaceGC::delta_capacity_until_GC(word_size);
- MetaspaceGC::inc_capacity_until_GC(delta_words);
+ size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size) * BytesPerWord;
+ MetaspaceGC::inc_capacity_until_GC(delta_bytes);
if (PrintGCDetails && Verbose) {
gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT
" to " SIZE_FORMAT, before_inc, MetaspaceGC::capacity_until_GC());
@@ -2774,8 +2912,8 @@ char* Metaspace::bottom() const {
return (char*)vsm()->current_chunk()->bottom();
}
-size_t Metaspace::used_words(MetadataType mdtype) const {
- // return vsm()->allocation_total();
+size_t Metaspace::used_words_slow(MetadataType mdtype) const {
+ // return vsm()->allocated_used_words();
return mdtype == ClassType ? class_vsm()->sum_used_in_chunks_in_use() :
vsm()->sum_used_in_chunks_in_use(); // includes overhead!
}
@@ -2790,11 +2928,19 @@ size_t Metaspace::free_words(MetadataType mdtype) const {
// have been made. Don't include space in the global freelist and
// in the space available in the dictionary which
// is already counted in some chunk.
-size_t Metaspace::capacity_words(MetadataType mdtype) const {
+size_t Metaspace::capacity_words_slow(MetadataType mdtype) const {
return mdtype == ClassType ? class_vsm()->sum_capacity_in_chunks_in_use() :
vsm()->sum_capacity_in_chunks_in_use();
}
+size_t Metaspace::used_bytes_slow(MetadataType mdtype) const {
+ return used_words_slow(mdtype) * BytesPerWord;
+}
+
+size_t Metaspace::capacity_bytes_slow(MetadataType mdtype) const {
+ return capacity_words_slow(mdtype) * BytesPerWord;
+}
+
void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) {
if (SafepointSynchronize::is_at_safepoint()) {
assert(Thread::current()->is_VM_thread(), "should be the VM thread");
@@ -2921,10 +3067,6 @@ void Metaspace::verify() {
}
void Metaspace::dump(outputStream* const out) const {
- if (UseMallocOnly) {
- // Just print usage for now
- out->print_cr("usage %d", used_words(Metaspace::NonClassType));
- }
out->print_cr("\nVirtual space manager: " INTPTR_FORMAT, vsm());
vsm()->dump(out);
out->print_cr("\nClass space manager: " INTPTR_FORMAT, class_vsm());
diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp
index 8d221914572..1108c79feab 100644
--- a/hotspot/src/share/vm/memory/metaspace.hpp
+++ b/hotspot/src/share/vm/memory/metaspace.hpp
@@ -111,6 +111,10 @@ class Metaspace : public CHeapObj {
SpaceManager* _class_vsm;
SpaceManager* class_vsm() const { return _class_vsm; }
+ // Allocate space for metadata of type mdtype. This is space
+ // within a Metachunk and is used by
+ // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS)
+ // which returns a Metablock.
MetaWord* allocate(size_t word_size, MetadataType mdtype);
// Virtual Space lists for both classes and other metadata
@@ -133,11 +137,14 @@ class Metaspace : public CHeapObj {
static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; }
char* bottom() const;
- size_t used_words(MetadataType mdtype) const;
+ size_t used_words_slow(MetadataType mdtype) const;
size_t free_words(MetadataType mdtype) const;
- size_t capacity_words(MetadataType mdtype) const;
+ size_t capacity_words_slow(MetadataType mdtype) const;
size_t waste_words(MetadataType mdtype) const;
+ size_t used_bytes_slow(MetadataType mdtype) const;
+ size_t capacity_bytes_slow(MetadataType mdtype) const;
+
static Metablock* allocate(ClassLoaderData* loader_data, size_t size,
bool read_only, MetadataType mdtype, TRAPS);
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
@@ -161,28 +168,81 @@ class Metaspace : public CHeapObj {
class MetaspaceAux : AllStatic {
// Statistics for class space and data space in metaspace.
- static size_t used_in_bytes(Metaspace::MetadataType mdtype);
+
+ // These methods iterate over the classloader data graph
+ // for the given Metaspace type. These are slow.
+ static size_t used_bytes_slow(Metaspace::MetadataType mdtype);
static size_t free_in_bytes(Metaspace::MetadataType mdtype);
- static size_t capacity_in_bytes(Metaspace::MetadataType mdtype);
+ static size_t capacity_bytes_slow(Metaspace::MetadataType mdtype);
+
+ // Iterates over the virtual space list.
static size_t reserved_in_bytes(Metaspace::MetadataType mdtype);
static size_t free_chunks_total(Metaspace::MetadataType mdtype);
static size_t free_chunks_total_in_bytes(Metaspace::MetadataType mdtype);
public:
- // Total of space allocated to metadata in all Metaspaces
- static size_t used_in_bytes() {
- return used_in_bytes(Metaspace::ClassType) +
- used_in_bytes(Metaspace::NonClassType);
+ // Running sum of space in all Metachunks that has been
+ // allocated to a Metaspace. This is used instead of
+ // iterating over all the classloaders
+ static size_t _allocated_capacity_words;
+ // Running sum of space in all Metachunks that have
+ // are being used for metadata.
+ static size_t _allocated_used_words;
+
+ public:
+ // Decrement and increment _allocated_capacity_words
+ static void dec_capacity(size_t words);
+ static void inc_capacity(size_t words);
+
+ // Decrement and increment _allocated_used_words
+ static void dec_used(size_t words);
+ static void inc_used(size_t words);
+
+ // Total of space allocated to metadata in all Metaspaces.
+ // This sums the space used in each Metachunk by
+ // iterating over the classloader data graph
+ static size_t used_bytes_slow() {
+ return used_bytes_slow(Metaspace::ClassType) +
+ used_bytes_slow(Metaspace::NonClassType);
}
- // Total of available space in all Metaspaces
- // Total of capacity allocated to all Metaspaces. This includes
- // space in Metachunks not yet allocated and in the Metachunk
- // freelist.
- static size_t capacity_in_bytes() {
- return capacity_in_bytes(Metaspace::ClassType) +
- capacity_in_bytes(Metaspace::NonClassType);
+ // Used by MetaspaceCounters
+ static size_t free_chunks_total();
+ static size_t free_chunks_total_in_bytes();
+
+ static size_t allocated_capacity_words() {
+ return _allocated_capacity_words;
+ }
+ static size_t allocated_capacity_bytes() {
+ return _allocated_capacity_words * BytesPerWord;
+ }
+
+ static size_t allocated_used_words() {
+ return _allocated_used_words;
+ }
+ static size_t allocated_used_bytes() {
+ return _allocated_used_words * BytesPerWord;
+ }
+
+ static size_t free_bytes();
+
+ // Total capacity in all Metaspaces
+ static size_t capacity_bytes_slow() {
+#ifdef PRODUCT
+ // Use allocated_capacity_bytes() in PRODUCT instead of this function.
+ guarantee(false, "Should not call capacity_bytes_slow() in the PRODUCT");
+#endif
+ size_t class_capacity = capacity_bytes_slow(Metaspace::ClassType);
+ size_t non_class_capacity = capacity_bytes_slow(Metaspace::NonClassType);
+ assert(allocated_capacity_bytes() == class_capacity + non_class_capacity,
+ err_msg("bad accounting: allocated_capacity_bytes() " SIZE_FORMAT
+ " class_capacity + non_class_capacity " SIZE_FORMAT
+ " class_capacity " SIZE_FORMAT " non_class_capacity " SIZE_FORMAT,
+ allocated_capacity_bytes(), class_capacity + non_class_capacity,
+ class_capacity, non_class_capacity));
+
+ return class_capacity + non_class_capacity;
}
// Total space reserved in all Metaspaces
@@ -201,6 +261,11 @@ class MetaspaceAux : AllStatic {
static void print_waste(outputStream* out);
static void dump(outputStream* out);
static void verify_free_chunks();
+ // Checks that the values returned by allocated_capacity_bytes() and
+ // capacity_bytes_slow() are the same.
+ static void verify_capacity();
+ static void verify_used();
+ static void verify_metrics();
};
// Metaspace are deallocated when their class loader are GC'ed.
@@ -235,7 +300,6 @@ class MetaspaceGC : AllStatic {
public:
static size_t capacity_until_GC() { return _capacity_until_GC; }
- static size_t capacity_until_GC_in_bytes() { return _capacity_until_GC * BytesPerWord; }
static void inc_capacity_until_GC(size_t v) { _capacity_until_GC += v; }
static void dec_capacity_until_GC(size_t v) {
_capacity_until_GC = _capacity_until_GC > v ? _capacity_until_GC - v : 0;
diff --git a/hotspot/src/share/vm/memory/metaspaceCounters.cpp b/hotspot/src/share/vm/memory/metaspaceCounters.cpp
index dc2f4f733aa..b2be29bca2f 100644
--- a/hotspot/src/share/vm/memory/metaspaceCounters.cpp
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.cpp
@@ -29,6 +29,16 @@
MetaspaceCounters* MetaspaceCounters::_metaspace_counters = NULL;
+size_t MetaspaceCounters::calc_total_capacity() {
+ // The total capacity is the sum of
+ // 1) capacity of Metachunks in use by all Metaspaces
+ // 2) unused space at the end of each Metachunk
+ // 3) space in the freelist
+ size_t total_capacity = MetaspaceAux::allocated_capacity_bytes()
+ + MetaspaceAux::free_bytes() + MetaspaceAux::free_chunks_total_in_bytes();
+ return total_capacity;
+}
+
MetaspaceCounters::MetaspaceCounters() :
_capacity(NULL),
_used(NULL),
@@ -36,8 +46,8 @@ MetaspaceCounters::MetaspaceCounters() :
if (UsePerfData) {
size_t min_capacity = MetaspaceAux::min_chunk_size();
size_t max_capacity = MetaspaceAux::reserved_in_bytes();
- size_t curr_capacity = MetaspaceAux::capacity_in_bytes();
- size_t used = MetaspaceAux::used_in_bytes();
+ size_t curr_capacity = calc_total_capacity();
+ size_t used = MetaspaceAux::allocated_used_bytes();
initialize(min_capacity, max_capacity, curr_capacity, used);
}
@@ -82,15 +92,13 @@ void MetaspaceCounters::initialize(size_t min_capacity,
void MetaspaceCounters::update_capacity() {
assert(UsePerfData, "Should not be called unless being used");
- assert(_capacity != NULL, "Should be initialized");
- size_t capacity_in_bytes = MetaspaceAux::capacity_in_bytes();
- _capacity->set_value(capacity_in_bytes);
+ size_t total_capacity = calc_total_capacity();
+ _capacity->set_value(total_capacity);
}
void MetaspaceCounters::update_used() {
assert(UsePerfData, "Should not be called unless being used");
- assert(_used != NULL, "Should be initialized");
- size_t used_in_bytes = MetaspaceAux::used_in_bytes();
+ size_t used_in_bytes = MetaspaceAux::allocated_used_bytes();
_used->set_value(used_in_bytes);
}
diff --git a/hotspot/src/share/vm/memory/metaspaceCounters.hpp b/hotspot/src/share/vm/memory/metaspaceCounters.hpp
index 4b6de646b60..46a9308888a 100644
--- a/hotspot/src/share/vm/memory/metaspaceCounters.hpp
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.hpp
@@ -37,6 +37,7 @@ class MetaspaceCounters: public CHeapObj {
size_t max_capacity,
size_t curr_capacity,
size_t used);
+ size_t calc_total_capacity();
public:
MetaspaceCounters();
~MetaspaceCounters();
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp
index 4f53114c6cd..5f0f152e975 100644
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp
@@ -376,18 +376,17 @@ void VM_PopulateDumpSharedSpace::doit() {
const char* fmt = "%s space: %9d [ %4.1f%% of total] out of %9d bytes [%4.1f%% used] at " PTR_FORMAT;
Metaspace* ro_space = _loader_data->ro_metaspace();
Metaspace* rw_space = _loader_data->rw_metaspace();
- const size_t BPW = BytesPerWord;
// Allocated size of each space (may not be all occupied)
- const size_t ro_alloced = ro_space->capacity_words(Metaspace::NonClassType) * BPW;
- const size_t rw_alloced = rw_space->capacity_words(Metaspace::NonClassType) * BPW;
+ const size_t ro_alloced = ro_space->capacity_bytes_slow(Metaspace::NonClassType);
+ const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType);
const size_t md_alloced = md_end-md_low;
const size_t mc_alloced = mc_end-mc_low;
const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced;
// Occupied size of each space.
- const size_t ro_bytes = ro_space->used_words(Metaspace::NonClassType) * BPW;
- const size_t rw_bytes = rw_space->used_words(Metaspace::NonClassType) * BPW;
+ const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType);
+ const size_t rw_bytes = rw_space->used_bytes_slow(Metaspace::NonClassType);
const size_t md_bytes = size_t(md_top - md_low);
const size_t mc_bytes = size_t(mc_top - mc_low);
From a4bf4b9be6d70055b8875d93b90b5475bcba99eb Mon Sep 17 00:00:00 2001
From: Athijegannathan Sundararajan
Date: Mon, 15 Apr 2013 20:12:50 +0530
Subject: [PATCH 008/134] 8012240: Array.prototype.map.call({length: -1, get
0(){throw 0}}, function(){}).length does not throw error
Reviewed-by: lagergren, jlaskey
---
.../internal/runtime/arrays/MapIterator.java | 3 +-
nashorn/test/script/basic/JDK-8012240.js | 47 +++++++++++++++++++
2 files changed, 48 insertions(+), 2 deletions(-)
create mode 100644 nashorn/test/script/basic/JDK-8012240.js
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java b/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
index be9bc23dd1e..fdf9d3a4047 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
@@ -66,8 +66,7 @@ class MapIterator extends ArrayLikeIterator
- *
- * @param cal java.util.GregorianCalendar used to create XMLGregorianCalendar
- *
- * @return XMLGregorianCalendar created from java.util.GregorianCalendar
- *
- * @throws NullPointerException If cal is null.
- */
- public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal);
+ /**
+ *
Create an XMLGregorianCalendar from a {@link GregorianCalendar}.
+ *
+ *
+ *
+ *
+ *
+ * Field by Field Conversion from
+ * {@link GregorianCalendar} to an {@link XMLGregorianCalendar}
+ *
+ *
+ *
+ *
java.util.GregorianCalendar field
+ *
javax.xml.datatype.XMLGregorianCalendar field
+ *
+ *
+ *
+ *
+ *
ERA == GregorianCalendar.BC ? -YEAR : YEAR
+ *
{@link XMLGregorianCalendar#setYear(int year)}
+ *
+ *
+ *
MONTH + 1
+ *
{@link XMLGregorianCalendar#setMonth(int month)}
+ *
+ *
+ *
DAY_OF_MONTH
+ *
{@link XMLGregorianCalendar#setDay(int day)}
+ *
+ *
+ *
HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND
+ *
{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}
*conversion loss of information. It is not possible to represent
+ * a java.util.GregorianCalendar daylight savings timezone id in the
+ * XML Schema 1.0 date/time datatype representation.
+ *
+ *
To compute the return value's TimeZone field,
+ *
+ *
when this.getTimezone() != FIELD_UNDEFINED,
+ * create a java.util.TimeZone with a custom timezone id
+ * using the this.getTimezone().
+ *
else use the GregorianCalendar default timezone value
+ * for the host is defined as specified by
+ * java.util.TimeZone.getDefault().
+ *
+ * @param cal java.util.GregorianCalendar used to create XMLGregorianCalendar
+ *
+ * @return XMLGregorianCalendar created from java.util.GregorianCalendar
+ *
+ * @throws NullPointerException If cal is null.
+ */
+ public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal);
- /**
- *
Constructor allowing for complete value spaces allowed by
- * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
- * builtin datatypes. Note that year parameter supports
- * arbitrarily large numbers and fractionalSecond has infinite
- * precision.
- *
+ /**
+ *
Constructor allowing for complete value spaces allowed by
+ * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
+ * builtin datatypes. Note that year parameter supports
+ * arbitrarily large numbers and fractionalSecond has infinite
+ * precision.
+ *
*
A null value indicates that field is not set.
*
- * @param year of XMLGregorianCalendar to be created.
- * @param month of XMLGregorianCalendar to be created.
- * @param day of XMLGregorianCalendar to be created.
- * @param hour of XMLGregorianCalendar to be created.
- * @param minute of XMLGregorianCalendar to be created.
- * @param second of XMLGregorianCalendar to be created.
- * @param fractionalSecond of XMLGregorianCalendar to be created.
- * @param timezone of XMLGregorianCalendar to be created.
- *
- * @return XMLGregorianCalendar created from specified values.
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- */
- public abstract XMLGregorianCalendar newXMLGregorianCalendar(
- final BigInteger year,
- final int month,
- final int day,
- final int hour,
- final int minute,
- final int second,
- final BigDecimal fractionalSecond,
- final int timezone);
+ * @param year of XMLGregorianCalendar to be created.
+ * @param month of XMLGregorianCalendar to be created.
+ * @param day of XMLGregorianCalendar to be created.
+ * @param hour of XMLGregorianCalendar to be created.
+ * @param minute of XMLGregorianCalendar to be created.
+ * @param second of XMLGregorianCalendar to be created.
+ * @param fractionalSecond of XMLGregorianCalendar to be created.
+ * @param timezone of XMLGregorianCalendar to be created.
+ *
+ * @return XMLGregorianCalendar created from specified values.
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ */
+ public abstract XMLGregorianCalendar newXMLGregorianCalendar(
+ final BigInteger year,
+ final int month,
+ final int day,
+ final int hour,
+ final int minute,
+ final int second,
+ final BigDecimal fractionalSecond,
+ final int timezone);
- /**
- *
Constructor of value spaces that a
- * java.util.GregorianCalendar instance would need to convert to an
- * XMLGregorianCalendar instance.
- *
- *
XMLGregorianCalendar eon and
- * fractionalSecond are set to null
- *
+ /**
+ *
Constructor of value spaces that a
+ * java.util.GregorianCalendar instance would need to convert to an
+ * XMLGregorianCalendar instance.
+ *
+ *
XMLGregorianCalendar eon and
+ * fractionalSecond are set to null
+ *
*
A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.
*
- * @param year of XMLGregorianCalendar to be created.
- * @param month of XMLGregorianCalendar to be created.
- * @param day of XMLGregorianCalendar to be created.
- * @param hour of XMLGregorianCalendar to be created.
- * @param minute of XMLGregorianCalendar to be created.
- * @param second of XMLGregorianCalendar to be created.
- * @param millisecond of XMLGregorianCalendar to be created.
- * @param timezone of XMLGregorianCalendar to be created.
- *
- * @return XMLGregorianCalendar created from specified values.
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- */
- public XMLGregorianCalendar newXMLGregorianCalendar(
- final int year,
- final int month,
- final int day,
- final int hour,
- final int minute,
- final int second,
- final int millisecond,
- final int timezone) {
+ * @param year of XMLGregorianCalendar to be created.
+ * @param month of XMLGregorianCalendar to be created.
+ * @param day of XMLGregorianCalendar to be created.
+ * @param hour of XMLGregorianCalendar to be created.
+ * @param minute of XMLGregorianCalendar to be created.
+ * @param second of XMLGregorianCalendar to be created.
+ * @param millisecond of XMLGregorianCalendar to be created.
+ * @param timezone of XMLGregorianCalendar to be created.
+ *
+ * @return XMLGregorianCalendar created from specified values.
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ */
+ public XMLGregorianCalendar newXMLGregorianCalendar(
+ final int year,
+ final int month,
+ final int day,
+ final int hour,
+ final int minute,
+ final int second,
+ final int millisecond,
+ final int timezone) {
- // year may be undefined
- BigInteger realYear = (year != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) year) : null;
+ // year may be undefined
+ BigInteger realYear = (year != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) year) : null;
- // millisecond may be undefined
- // millisecond must be >= 0 millisecond <= 1000
- BigDecimal realMillisecond = null; // undefined value
- if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
- if (millisecond < 0 || millisecond > 1000) {
- throw new IllegalArgumentException(
- "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendar("
- + "int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone)"
- + "with invalid millisecond: " + millisecond
- );
- }
+ // millisecond may be undefined
+ // millisecond must be >= 0 millisecond <= 1000
+ BigDecimal realMillisecond = null; // undefined value
+ if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
+ if (millisecond < 0 || millisecond > 1000) {
+ throw new IllegalArgumentException(
+ "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendar("
+ + "int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone)"
+ + "with invalid millisecond: " + millisecond
+ );
+ }
- realMillisecond = BigDecimal.valueOf((long) millisecond).movePointLeft(3);
- }
+ realMillisecond = BigDecimal.valueOf((long) millisecond).movePointLeft(3);
+ }
- return newXMLGregorianCalendar(
- realYear,
- month,
- day,
- hour,
- minute,
- second,
- realMillisecond,
- timezone
- );
- }
+ return newXMLGregorianCalendar(
+ realYear,
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ realMillisecond,
+ timezone
+ );
+ }
- /**
- *
Create a Java representation of XML Schema builtin datatype date or g*.
- *
- *
For example, an instance of gYear can be created invoking this factory
- * with month and day parameters set to
- * {@link DatatypeConstants#FIELD_UNDEFINED}.
- *
+ /**
+ *
Create a Java representation of XML Schema builtin datatype date or g*.
+ *
+ *
For example, an instance of gYear can be created invoking this factory
+ * with month and day parameters set to
+ * {@link DatatypeConstants#FIELD_UNDEFINED}.
+ *
*
A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.
*
- * @param year of XMLGregorianCalendar to be created.
- * @param month of XMLGregorianCalendar to be created.
- * @param day of XMLGregorianCalendar to be created.
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return XMLGregorianCalendar created from parameter values.
- *
- * @see DatatypeConstants#FIELD_UNDEFINED
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- */
- public XMLGregorianCalendar newXMLGregorianCalendarDate(
- final int year,
- final int month,
- final int day,
- final int timezone) {
+ * @param year of XMLGregorianCalendar to be created.
+ * @param month of XMLGregorianCalendar to be created.
+ * @param day of XMLGregorianCalendar to be created.
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return XMLGregorianCalendar created from parameter values.
+ *
+ * @see DatatypeConstants#FIELD_UNDEFINED
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ */
+ public XMLGregorianCalendar newXMLGregorianCalendarDate(
+ final int year,
+ final int month,
+ final int day,
+ final int timezone) {
- return newXMLGregorianCalendar(
- year,
- month,
- day,
- DatatypeConstants.FIELD_UNDEFINED, // hour
- DatatypeConstants.FIELD_UNDEFINED, // minute
- DatatypeConstants.FIELD_UNDEFINED, // second
- DatatypeConstants.FIELD_UNDEFINED, // millisecond
- timezone);
- }
+ return newXMLGregorianCalendar(
+ year,
+ month,
+ day,
+ DatatypeConstants.FIELD_UNDEFINED, // hour
+ DatatypeConstants.FIELD_UNDEFINED, // minute
+ DatatypeConstants.FIELD_UNDEFINED, // second
+ DatatypeConstants.FIELD_UNDEFINED, // millisecond
+ timezone);
+ }
- /**
- *
Create a Java instance of XML Schema builtin datatype time.
- *
+ /**
+ *
Create a Java instance of XML Schema builtin datatype time.
+ *
*
A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.
*
- * @param hours number of hours
- * @param minutes number of minutes
- * @param seconds number of seconds
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return XMLGregorianCalendar created from parameter values.
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- *
- * @see DatatypeConstants#FIELD_UNDEFINED
- */
- public XMLGregorianCalendar newXMLGregorianCalendarTime(
- final int hours,
- final int minutes,
- final int seconds,
- final int timezone) {
+ * @param hours number of hours
+ * @param minutes number of minutes
+ * @param seconds number of seconds
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return XMLGregorianCalendar created from parameter values.
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ *
+ * @see DatatypeConstants#FIELD_UNDEFINED
+ */
+ public XMLGregorianCalendar newXMLGregorianCalendarTime(
+ final int hours,
+ final int minutes,
+ final int seconds,
+ final int timezone) {
- return newXMLGregorianCalendar(
- DatatypeConstants.FIELD_UNDEFINED, // Year
- DatatypeConstants.FIELD_UNDEFINED, // Month
- DatatypeConstants.FIELD_UNDEFINED, // Day
- hours,
- minutes,
- seconds,
- DatatypeConstants.FIELD_UNDEFINED, //Millisecond
- timezone);
- }
+ return newXMLGregorianCalendar(
+ DatatypeConstants.FIELD_UNDEFINED, // Year
+ DatatypeConstants.FIELD_UNDEFINED, // Month
+ DatatypeConstants.FIELD_UNDEFINED, // Day
+ hours,
+ minutes,
+ seconds,
+ DatatypeConstants.FIELD_UNDEFINED, //Millisecond
+ timezone);
+ }
- /**
- *
Create a Java instance of XML Schema builtin datatype time.
- *
+ /**
+ *
Create a Java instance of XML Schema builtin datatype time.
+ *
*
A null value indicates that field is not set.
*
A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.
*
- * @param hours number of hours
- * @param minutes number of minutes
- * @param seconds number of seconds
- * @param fractionalSecond value of null indicates that this optional field is not set.
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return XMLGregorianCalendar created from parameter values.
- *
- * @see DatatypeConstants#FIELD_UNDEFINED
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- */
- public XMLGregorianCalendar newXMLGregorianCalendarTime(
- final int hours,
- final int minutes,
- final int seconds,
- final BigDecimal fractionalSecond,
- final int timezone) {
+ * @param hours number of hours
+ * @param minutes number of minutes
+ * @param seconds number of seconds
+ * @param fractionalSecond value of null indicates that this optional field is not set.
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return XMLGregorianCalendar created from parameter values.
+ *
+ * @see DatatypeConstants#FIELD_UNDEFINED
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ */
+ public XMLGregorianCalendar newXMLGregorianCalendarTime(
+ final int hours,
+ final int minutes,
+ final int seconds,
+ final BigDecimal fractionalSecond,
+ final int timezone) {
- return newXMLGregorianCalendar(
- null, // year
- DatatypeConstants.FIELD_UNDEFINED, // month
- DatatypeConstants.FIELD_UNDEFINED, // day
- hours,
- minutes,
- seconds,
- fractionalSecond,
- timezone);
- }
+ return newXMLGregorianCalendar(
+ null, // year
+ DatatypeConstants.FIELD_UNDEFINED, // month
+ DatatypeConstants.FIELD_UNDEFINED, // day
+ hours,
+ minutes,
+ seconds,
+ fractionalSecond,
+ timezone);
+ }
- /**
- *
Create a Java instance of XML Schema builtin datatype time.
- *
+ /**
+ *
Create a Java instance of XML Schema builtin datatype time.
+ *
*
A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.
*
- * @param hours number of hours
- * @param minutes number of minutes
- * @param seconds number of seconds
- * @param milliseconds number of milliseconds
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return XMLGregorianCalendar created from parameter values.
- *
- * @see DatatypeConstants#FIELD_UNDEFINED
- *
- * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
- * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
- * or if the composite values constitute an invalid XMLGregorianCalendar instance
- * as determined by {@link XMLGregorianCalendar#isValid()}.
- */
- public XMLGregorianCalendar newXMLGregorianCalendarTime(
- final int hours,
- final int minutes,
- final int seconds,
- final int milliseconds,
- final int timezone) {
+ * @param hours number of hours
+ * @param minutes number of minutes
+ * @param seconds number of seconds
+ * @param milliseconds number of milliseconds
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return XMLGregorianCalendar created from parameter values.
+ *
+ * @see DatatypeConstants#FIELD_UNDEFINED
+ *
+ * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field
+ * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar}
+ * or if the composite values constitute an invalid XMLGregorianCalendar instance
+ * as determined by {@link XMLGregorianCalendar#isValid()}.
+ */
+ public XMLGregorianCalendar newXMLGregorianCalendarTime(
+ final int hours,
+ final int minutes,
+ final int seconds,
+ final int milliseconds,
+ final int timezone) {
- // millisecond may be undefined
- // millisecond must be >= 0 millisecond <= 1000
- BigDecimal realMilliseconds = null; // undefined value
- if (milliseconds != DatatypeConstants.FIELD_UNDEFINED) {
- if (milliseconds < 0 || milliseconds > 1000) {
- throw new IllegalArgumentException(
- "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendarTime("
- + "int hours, int minutes, int seconds, int milliseconds, int timezone)"
- + "with invalid milliseconds: " + milliseconds
- );
- }
+ // millisecond may be undefined
+ // millisecond must be >= 0 millisecond <= 1000
+ BigDecimal realMilliseconds = null; // undefined value
+ if (milliseconds != DatatypeConstants.FIELD_UNDEFINED) {
+ if (milliseconds < 0 || milliseconds > 1000) {
+ throw new IllegalArgumentException(
+ "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendarTime("
+ + "int hours, int minutes, int seconds, int milliseconds, int timezone)"
+ + "with invalid milliseconds: " + milliseconds
+ );
+ }
- realMilliseconds = BigDecimal.valueOf((long) milliseconds).movePointLeft(3);
- }
+ realMilliseconds = BigDecimal.valueOf((long) milliseconds).movePointLeft(3);
+ }
- return newXMLGregorianCalendarTime(
- hours,
- minutes,
- seconds,
- realMilliseconds,
- timezone
- );
- }
+ return newXMLGregorianCalendarTime(
+ hours,
+ minutes,
+ seconds,
+ realMilliseconds,
+ timezone
+ );
+ }
}
diff --git a/jaxp/src/javax/xml/datatype/FactoryFinder.java b/jaxp/src/javax/xml/datatype/FactoryFinder.java
index deb47eaa11c..ce982157240 100644
--- a/jaxp/src/javax/xml/datatype/FactoryFinder.java
+++ b/jaxp/src/javax/xml/datatype/FactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2013, 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
@@ -26,14 +26,12 @@
package javax.xml.datatype;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
import java.util.Properties;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.net.URL;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
*
Implements pluggable Datatypes.
@@ -54,19 +52,19 @@ class FactoryFinder {
/**
* Cache for properties in java.home/lib/jaxp.properties
*/
- static Properties cacheProps = new Properties();
+ private final static Properties cacheProps = new Properties();
/**
* Flag indicating if properties from java.home/lib/jaxp.properties
* have been cached.
*/
- static volatile boolean firstTime = true;
+ private static volatile boolean firstTime = true;
/**
* Security support class use to check access control before
* getting certain system resources.
*/
- static SecuritySupport ss = new SecuritySupport();
+ private final static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output
static {
@@ -99,31 +97,31 @@ class FactoryFinder {
*
* Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/
- static private Class getProviderClass(String className, ClassLoader cl,
+ static private Class> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
try {
if (cl == null) {
if (useBSClsLoader) {
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
}
else {
throw e1;
@@ -135,6 +133,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -144,16 +145,19 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback)
+ throws DatatypeConfigurationException
{
- return newInstance(className, cl, doFallback, false);
+ return newInstance(type, className, cl, doFallback, false);
}
/**
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -166,9 +170,12 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl,
+ boolean doFallback, boolean useBSClsLoader)
+ throws DatatypeConfigurationException
{
+ assert type != null;
+
// make sure we have access to restricted packages
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@@ -178,20 +185,23 @@ class FactoryFinder {
}
try {
- Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ Class> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ if (!type.isAssignableFrom(providerClass)) {
+ throw new ClassCastException(className + " cannot be cast to " + type.getName());
+ }
Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl);
}
- return instance;
+ return type.cast(instance);
}
catch (ClassNotFoundException x) {
- throw new ConfigurationError(
+ throw new DatatypeConfigurationException(
"Provider " + className + " not found", x);
}
catch (Exception x) {
- throw new ConfigurationError(
+ throw new DatatypeConfigurationException(
"Provider " + className + " could not be instantiated: " + x,
x);
}
@@ -202,16 +212,17 @@ class FactoryFinder {
* entry point.
* @return Class object of factory, never null
*
- * @param factoryId Name of the factory to find, same as
- * a property name
+ * @param type Base class / Service interface of the
+ * factory to find.
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
*
* Package private so this code can be shared.
*/
- static Object find(String factoryId, String fallbackClassName)
- throws ConfigurationError
+ static T find(Class type, String fallbackClassName)
+ throws DatatypeConfigurationException
{
+ final String factoryId = type.getName();
dPrint("find factoryId =" + factoryId);
// Use the system property first
@@ -219,7 +230,7 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint("found system property, value=" + systemProp);
- return newInstance(systemProp, null, true);
+ return newInstance(type, systemProp, null, true);
}
}
catch (SecurityException se) {
@@ -228,7 +239,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties
try {
- String factoryClassName = null;
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
@@ -243,11 +253,11 @@ class FactoryFinder {
}
}
}
- factoryClassName = cacheProps.getProperty(factoryId);
+ final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
- return newInstance(factoryClassName, null, true);
+ return newInstance(type, factoryClassName, null, true);
}
}
catch (Exception ex) {
@@ -255,112 +265,46 @@ class FactoryFinder {
}
// Try Jar Service Provider Mechanism
- Object provider = findJarServiceProvider(factoryId);
+ final T provider = findServiceProvider(type);
if (provider != null) {
return provider;
}
if (fallbackClassName == null) {
- throw new ConfigurationError(
- "Provider for " + factoryId + " cannot be found", null);
+ throw new DatatypeConfigurationException(
+ "Provider for " + factoryId + " cannot be found");
}
dPrint("loaded from fallback value: " + fallbackClassName);
- return newInstance(fallbackClassName, null, true);
+ return newInstance(type, fallbackClassName, null, true);
}
/*
- * Try to find provider using Jar Service Provider Mechanism
+ * Try to find provider using the ServiceLoader API
+ *
+ * @param type Base class / Service interface of the factory to find.
*
* @return instance of provider class if found or null
*/
- private static Object findJarServiceProvider(String factoryId)
- throws ConfigurationError
+ private static T findServiceProvider(final Class type)
+ throws DatatypeConfigurationException
{
- String serviceId = "META-INF/services/" + factoryId;
- InputStream is = null;
-
- // First try the Context ClassLoader
- ClassLoader cl = ss.getContextClassLoader();
- boolean useBSClsLoader = false;
- if (cl != null) {
- is = ss.getResourceAsStream(cl, serviceId);
-
- // If no provider found then try the current ClassLoader
- if (is == null) {
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
- } else {
- // No Context ClassLoader, try the current ClassLoader
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
-
- if (is == null) {
- // No provider found
- return null;
- }
-
- if (debug) { // Extra check to avoid computing cl strings
- dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
- }
-
- BufferedReader rd;
try {
- rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- }
- catch (java.io.UnsupportedEncodingException e) {
- rd = new BufferedReader(new InputStreamReader(is));
- }
-
- String factoryClassName = null;
- try {
- // XXX Does not handle all possible input as specified by the
- // Jar Service Provider specification
- factoryClassName = rd.readLine();
- rd.close();
- } catch (IOException x) {
- // No provider found
- return null;
- }
-
- if (factoryClassName != null && !"".equals(factoryClassName)) {
- dPrint("found in resource, value=" + factoryClassName);
-
- // Note: here we do not want to fall back to the current
- // ClassLoader because we want to avoid the case where the
- // resource file was found using one ClassLoader and the
- // provider class was instantiated using a different one.
- return newInstance(factoryClassName, cl, false, useBSClsLoader);
- }
-
- // No provider found
- return null;
- }
-
- static class ConfigurationError extends Error {
- private Exception exception;
-
- /**
- * Construct a new instance with the specified detail string and
- * exception.
- */
- ConfigurationError(String msg, Exception x) {
- super(msg);
- this.exception = x;
- }
-
- Exception getException() {
- return exception;
- }
- /**
- * use the exception chaining mechanism of JDK1.4
- */
- @Override
- public Throwable getCause() {
- return exception;
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public T run() {
+ final ServiceLoader serviceLoader = ServiceLoader.load(type);
+ final Iterator iterator = serviceLoader.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return null;
+ }
+ }
+ });
+ } catch(ServiceConfigurationError e) {
+ final DatatypeConfigurationException error =
+ new DatatypeConfigurationException(
+ "Provider for " + type + " cannot be found", e);
+ throw error;
}
}
diff --git a/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java b/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java
index 98db9beed5d..0ef1c13cecb 100644
--- a/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java
+++ b/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2013, 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
@@ -40,9 +40,6 @@ import javax.xml.validation.Schema;
public abstract class DocumentBuilderFactory {
- /** The default property name according to the JAXP spec */
- private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.DocumentBuilderFactory";
-
private boolean validating = false;
private boolean namespaceAware = false;
private boolean whitespace = false;
@@ -50,8 +47,6 @@ public abstract class DocumentBuilderFactory {
private boolean ignoreComments = false;
private boolean coalescing = false;
- private boolean canonicalState = false;
-
/**
*
Protected constructor to prevent instantiation.
* Use {@link #newInstance()}.
@@ -85,14 +80,12 @@ public abstract class DocumentBuilderFactory {
* of any property in jaxp.properties after it has been read for the first time.
*
*
- * Use the Services API (as detailed in the JAR specification), if
- * available, to determine the classname. The Services API will look
- * for a classname in the file
- * META-INF/services/javax.xml.parsers.DocumentBuilderFactory
- * in jars available to the runtime.
+ * Uses the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
*
*
- * Platform default DocumentBuilderFactory instance.
+ * Otherwise, the system-default implementation is returned.
*
*
*
@@ -113,21 +106,16 @@ public abstract class DocumentBuilderFactory {
*
* @return New instance of a DocumentBuilderFactory
*
- * @throws FactoryConfigurationError if the implementation is not
- * available or cannot be instantiated.
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static DocumentBuilderFactory newInstance() {
- try {
- return (DocumentBuilderFactory) FactoryFinder.find(
+ return FactoryFinder.find(
/* The default property name according to the JAXP spec */
- "javax.xml.parsers.DocumentBuilderFactory",
+ DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
-
}
/**
@@ -165,13 +153,9 @@ public abstract class DocumentBuilderFactory {
* @since 1.6
*/
public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader){
- try {
//do not fallback if given classloader can't find the class, throw exception
- return (DocumentBuilderFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ return FactoryFinder.newInstance(DocumentBuilderFactory.class,
+ factoryClassName, classLoader, false);
}
/**
@@ -391,75 +375,64 @@ public abstract class DocumentBuilderFactory {
public abstract Object getAttribute(String name)
throws IllegalArgumentException;
- /**
- *
Set a feature for this DocumentBuilderFactory and DocumentBuilders created by this factory.
- *
- *
- * Feature names are fully qualified {@link java.net.URI}s.
- * Implementations may define their own features.
- * A {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the
- * DocumentBuilders it creates cannot support the feature.
- * It is possible for a DocumentBuilderFactory to expose a feature value but be unable to change its state.
- *
- *
- *
- * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
- * When the feature is:
- *
- *
- * true: the implementation will limit XML processing to conform to implementation limits.
- * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources.
- * If XML processing is limited for security reasons, it will be reported via a call to the registered
- * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
- * See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}.
- *
- *
- * false: the implementation will processing XML according to the XML specifications without
- * regard to possible implementation limits.
- *
- *
- *
- * @param name Feature name.
- * @param value Is feature state true or false.
- *
- * @throws ParserConfigurationException if this DocumentBuilderFactory or the DocumentBuilders
- * it creates cannot support this feature.
- * @throws NullPointerException If the name parameter is null.
- */
- public abstract void setFeature(String name, boolean value)
- throws ParserConfigurationException;
-
- /**
- *
Get the state of the named feature.
- *
- *
- * Feature names are fully qualified {@link java.net.URI}s.
- * Implementations may define their own features.
- * An {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the
- * DocumentBuilders it creates cannot support the feature.
- * It is possible for an DocumentBuilderFactory to expose a feature value but be unable to change its state.
- *
- *
- * @param name Feature name.
- *
- * @return State of the named feature.
- *
- * @throws ParserConfigurationException if this DocumentBuilderFactory
- * or the DocumentBuilders it creates cannot support this feature.
- */
- public abstract boolean getFeature(String name)
- throws ParserConfigurationException;
-
-
- /**
Get current state of canonicalization.
+ /**
+ *
Set a feature for this DocumentBuilderFactory and DocumentBuilders created by this factory.
*
- * @return current state canonicalization control
+ *
+ * Feature names are fully qualified {@link java.net.URI}s.
+ * Implementations may define their own features.
+ * A {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the
+ * DocumentBuilders it creates cannot support the feature.
+ * It is possible for a DocumentBuilderFactory to expose a feature value but be unable to change its state.
+ *
+ *
+ *
+ * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
+ * When the feature is:
+ *
+ *
+ * true: the implementation will limit XML processing to conform to implementation limits.
+ * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources.
+ * If XML processing is limited for security reasons, it will be reported via a call to the registered
+ * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
+ * See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}.
+ *
+ *
+ * false: the implementation will processing XML according to the XML specifications without
+ * regard to possible implementation limits.
+ *
+ *
+ *
+ * @param name Feature name.
+ * @param value Is feature state true or false.
+ *
+ * @throws ParserConfigurationException if this DocumentBuilderFactory or the DocumentBuilders
+ * it creates cannot support this feature.
+ * @throws NullPointerException If the name parameter is null.
*/
- /*
- public boolean getCanonicalization() {
- return canonicalState;
- }
- */
+ public abstract void setFeature(String name, boolean value)
+ throws ParserConfigurationException;
+
+ /**
+ *
Get the state of the named feature.
+ *
+ *
+ * Feature names are fully qualified {@link java.net.URI}s.
+ * Implementations may define their own features.
+ * An {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the
+ * DocumentBuilders it creates cannot support the feature.
+ * It is possible for an DocumentBuilderFactory to expose a feature value but be unable to change its state.
+ *
+ *
+ * @param name Feature name.
+ *
+ * @return State of the named feature.
+ *
+ * @throws ParserConfigurationException if this DocumentBuilderFactory
+ * or the DocumentBuilders it creates cannot support this feature.
+ */
+ public abstract boolean getFeature(String name)
+ throws ParserConfigurationException;
/**
@@ -488,17 +461,6 @@ public abstract class DocumentBuilderFactory {
}
- /*
Set canonicalization control to true or
- * false.
- *
- * @param state of canonicalization
- */
- /*
- public void setCanonicalization(boolean state) {
- canonicalState = state;
- }
- */
-
/**
*
Set the {@link Schema} to be used by parsers created
* from this factory.
diff --git a/jaxp/src/javax/xml/parsers/FactoryFinder.java b/jaxp/src/javax/xml/parsers/FactoryFinder.java
index 214f87f2239..9e186f512ef 100644
--- a/jaxp/src/javax/xml/parsers/FactoryFinder.java
+++ b/jaxp/src/javax/xml/parsers/FactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -25,15 +25,16 @@
package javax.xml.parsers;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
- *
Implements pluggable Datatypes.
+ *
Implements pluggable Parsers.
*
*
This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.
@@ -51,7 +52,7 @@ class FactoryFinder {
/**
* Cache for properties in java.home/lib/jaxp.properties
*/
- static Properties cacheProps = new Properties();
+ private static final Properties cacheProps = new Properties();
/**
* Flag indicating if properties from java.home/lib/jaxp.properties
@@ -63,7 +64,7 @@ class FactoryFinder {
* Security support class use to check access control before
* getting certain system resources.
*/
- static SecuritySupport ss = new SecuritySupport();
+ private static final SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output
static {
@@ -96,31 +97,31 @@ class FactoryFinder {
*
* Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/
- static private Class getProviderClass(String className, ClassLoader cl,
+ static private Class> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
try {
if (cl == null) {
if (useBSClsLoader) {
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
}
else {
throw e1;
@@ -132,6 +133,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -141,16 +145,20 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl,
+ boolean doFallback)
+ throws FactoryConfigurationError
{
- return newInstance(className, cl, doFallback, false);
+ return newInstance(type, className, cl, doFallback, false);
}
/**
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -163,9 +171,11 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl,
+ boolean doFallback, boolean useBSClsLoader)
+ throws FactoryConfigurationError
{
+ assert type != null;
// make sure we have access to restricted packages
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@@ -175,22 +185,24 @@ class FactoryFinder {
}
try {
- Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ Class> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ if (!type.isAssignableFrom(providerClass)) {
+ throw new ClassCastException(className + " cannot be cast to " + type.getName());
+ }
Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl);
}
- return instance;
+ return type.cast(instance);
}
catch (ClassNotFoundException x) {
- throw new ConfigurationError(
- "Provider " + className + " not found", x);
+ throw new FactoryConfigurationError(x,
+ "Provider " + className + " not found");
}
catch (Exception x) {
- throw new ConfigurationError(
- "Provider " + className + " could not be instantiated: " + x,
- x);
+ throw new FactoryConfigurationError(x,
+ "Provider " + className + " could not be instantiated: " + x);
}
}
@@ -199,16 +211,17 @@ class FactoryFinder {
* entry point.
* @return Class object of factory, never null
*
- * @param factoryId Name of the factory to find, same as
- * a property name
+ * @param type Base class / Service interface of the
+ * factory to find.
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
*
* Package private so this code can be shared.
*/
- static Object find(String factoryId, String fallbackClassName)
- throws ConfigurationError
+ static T find(Class type, String fallbackClassName)
+ throws FactoryConfigurationError
{
+ final String factoryId = type.getName();
dPrint("find factoryId =" + factoryId);
// Use the system property first
@@ -216,7 +229,7 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint("found system property, value=" + systemProp);
- return newInstance(systemProp, null, true);
+ return newInstance(type, systemProp, null, true);
}
}
catch (SecurityException se) {
@@ -225,7 +238,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties
try {
- String factoryClassName = null;
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
@@ -240,11 +252,11 @@ class FactoryFinder {
}
}
}
- factoryClassName = cacheProps.getProperty(factoryId);
+ final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
- return newInstance(factoryClassName, null, true);
+ return newInstance(type, factoryClassName, null, true);
}
}
catch (Exception ex) {
@@ -252,112 +264,52 @@ class FactoryFinder {
}
// Try Jar Service Provider Mechanism
- Object provider = findJarServiceProvider(factoryId);
+ T provider = findServiceProvider(type);
if (provider != null) {
return provider;
}
if (fallbackClassName == null) {
- throw new ConfigurationError(
- "Provider for " + factoryId + " cannot be found", null);
+ throw new FactoryConfigurationError(
+ "Provider for " + factoryId + " cannot be found");
}
dPrint("loaded from fallback value: " + fallbackClassName);
- return newInstance(fallbackClassName, null, true);
+ return newInstance(type, fallbackClassName, null, true);
}
/*
- * Try to find provider using Jar Service Provider Mechanism
+ * Try to find provider using the ServiceLoader API
+ *
+ * @param type Base class / Service interface of the factory to find.
*
* @return instance of provider class if found or null
*/
- private static Object findJarServiceProvider(String factoryId)
- throws ConfigurationError
- {
- String serviceId = "META-INF/services/" + factoryId;
- InputStream is = null;
-
- // First try the Context ClassLoader
- ClassLoader cl = ss.getContextClassLoader();
- boolean useBSClsLoader = false;
- if (cl != null) {
- is = ss.getResourceAsStream(cl, serviceId);
-
- // If no provider found then try the current ClassLoader
- if (is == null) {
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
- } else {
- // No Context ClassLoader, try the current ClassLoader
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
-
- if (is == null) {
- // No provider found
- return null;
- }
-
- if (debug) { // Extra check to avoid computing cl strings
- dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
- }
-
- BufferedReader rd;
+ private static T findServiceProvider(final Class type) {
try {
- rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- }
- catch (java.io.UnsupportedEncodingException e) {
- rd = new BufferedReader(new InputStreamReader(is));
- }
-
- String factoryClassName = null;
- try {
- // XXX Does not handle all possible input as specified by the
- // Jar Service Provider specification
- factoryClassName = rd.readLine();
- rd.close();
- } catch (IOException x) {
- // No provider found
- return null;
- }
-
- if (factoryClassName != null && !"".equals(factoryClassName)) {
- dPrint("found in resource, value=" + factoryClassName);
-
- // Note: here we do not want to fall back to the current
- // ClassLoader because we want to avoid the case where the
- // resource file was found using one ClassLoader and the
- // provider class was instantiated using a different one.
- return newInstance(factoryClassName, cl, false, useBSClsLoader);
- }
-
- // No provider found
- return null;
- }
-
- static class ConfigurationError extends Error {
- private Exception exception;
-
- /**
- * Construct a new instance with the specified detail string and
- * exception.
- */
- ConfigurationError(String msg, Exception x) {
- super(msg);
- this.exception = x;
- }
-
- Exception getException() {
- return exception;
- }
- /**
- * use the exception chaining mechanism of JDK1.4
- */
- @Override
- public Throwable getCause() {
- return exception;
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public T run() {
+ final ServiceLoader serviceLoader = ServiceLoader.load(type);
+ final Iterator iterator = serviceLoader.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return null;
+ }
+ }
+ });
+ } catch(ServiceConfigurationError e) {
+ // It is not possible to wrap an error directly in
+ // FactoryConfigurationError - so we need to wrap the
+ // ServiceConfigurationError in a RuntimeException.
+ // The alternative would be to modify the logic in
+ // FactoryConfigurationError to allow setting a
+ // Throwable as the cause, but that could cause
+ // compatibility issues down the road.
+ final RuntimeException x = new RuntimeException(
+ "Provider for " + type + " cannot be created", e);
+ final FactoryConfigurationError error =
+ new FactoryConfigurationError(x, x.getMessage());
+ throw error;
}
}
diff --git a/jaxp/src/javax/xml/parsers/SAXParserFactory.java b/jaxp/src/javax/xml/parsers/SAXParserFactory.java
index 95c78042767..a7aef97bdf3 100644
--- a/jaxp/src/javax/xml/parsers/SAXParserFactory.java
+++ b/jaxp/src/javax/xml/parsers/SAXParserFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2013, 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
@@ -26,7 +26,6 @@
package javax.xml.parsers;
import javax.xml.validation.Schema;
-
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
@@ -42,8 +41,6 @@ import org.xml.sax.SAXNotSupportedException;
*
*/
public abstract class SAXParserFactory {
- /** The default property name according to the JAXP spec */
- private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory";
/**
*
Should Parsers be validating?
@@ -87,14 +84,12 @@ public abstract class SAXParserFactory {
* of any property in jaxp.properties after it has been read for the first time.
*
*
- * Use the Services API (as detailed in the JAR specification), if
- * available, to determine the classname. The Services API will look
- * for a classname in the file
- * META-INF/services/javax.xml.parsers.SAXParserFactory
- * in jars available to the runtime.
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
*
*
- * Platform default SAXParserFactory instance.
+ * Otherwise the system-default implementation is returned.
*
*
*
@@ -109,7 +104,7 @@ public abstract class SAXParserFactory {
* this method to print a lot of debug messages
* to System.err about what it is doing and where it is looking at.
*
- *
If you have problems loading {@link DocumentBuilder}s, try:
+ *
If you have problems loading {@link SAXParser}s, try:
*
* java -Djaxp.debug=1 YourProgram ....
*
@@ -117,21 +112,17 @@ public abstract class SAXParserFactory {
*
* @return A new instance of a SAXParserFactory.
*
- * @throws FactoryConfigurationError if the implementation is
- * not available or cannot be instantiated.
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static SAXParserFactory newInstance() {
- try {
- return (SAXParserFactory) FactoryFinder.find(
+ return FactoryFinder.find(
/* The default property name according to the JAXP spec */
- "javax.xml.parsers.SAXParserFactory",
+ SAXParserFactory.class,
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
}
/**
@@ -169,13 +160,9 @@ public abstract class SAXParserFactory {
* @since 1.6
*/
public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader){
- try {
//do not fallback if given classloader can't find the class, throw exception
- return (SAXParserFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ return FactoryFinder.newInstance(SAXParserFactory.class,
+ factoryClassName, classLoader, false);
}
/**
@@ -266,22 +253,22 @@ public abstract class SAXParserFactory {
* A list of the core features and properties can be found at
* http://www.saxproject.org/
*
- *
All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
- * When the feature is
- *
- *
- * true: the implementation will limit XML processing to conform to implementation limits.
- * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources.
- * If XML processing is limited for security reasons, it will be reported via a call to the registered
- * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
- * See {@link SAXParser} parse methods for handler specification.
- *
- *
- * When the feature is false, the implementation will processing XML according to the XML specifications without
- * regard to possible implementation limits.
- *
- *
- *
+ *
All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
+ * When the feature is
+ *
+ *
+ * true: the implementation will limit XML processing to conform to implementation limits.
+ * Examples include entity expansion limits and XML Schema constructs that would consume large amounts of resources.
+ * If XML processing is limited for security reasons, it will be reported via a call to the registered
+ * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
+ * See {@link SAXParser} parse methods for handler specification.
+ *
+ *
+ * When the feature is false, the implementation will processing XML according to the XML specifications without
+ * regard to possible implementation limits.
+ *
+ *
+ *
* @param name The name of the feature to be set.
* @param value The value of the feature to be set.
*
@@ -320,17 +307,6 @@ public abstract class SAXParserFactory {
SAXNotSupportedException;
-
- /*
Get current state of canonicalization.
- *
- * @return current state canonicalization control
- */
- /*
- public boolean getCanonicalization() {
- return canonicalState;
- }
- */
-
/**
* Gets the {@link Schema} object specified through
* the {@link #setSchema(Schema schema)} method.
@@ -357,17 +333,6 @@ public abstract class SAXParserFactory {
);
}
- /**
Set canonicalization control to true or
- * false.
- *
- * @param state of canonicalization
- */
- /*
- public void setCanonicalization(boolean state) {
- canonicalState = state;
- }
- */
-
/**
*
Set the {@link Schema} to be used by parsers created
* from this factory.
@@ -400,7 +365,7 @@ public abstract class SAXParserFactory {
* Such configuration will cause a {@link SAXException}
* exception when those properties are set on a {@link SAXParser}.
*
- *
Note for implmentors
+ *
Note for implementors
*
* A parser must be able to work with any {@link Schema}
* implementation. However, parsers and schemas are allowed
diff --git a/jaxp/src/javax/xml/stream/FactoryFinder.java b/jaxp/src/javax/xml/stream/FactoryFinder.java
index bcfeba10e04..68f4ef21d66 100644
--- a/jaxp/src/javax/xml/stream/FactoryFinder.java
+++ b/jaxp/src/javax/xml/stream/FactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -25,15 +25,16 @@
package javax.xml.stream;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
- *
Implements pluggable Datatypes.
+ *
Implements pluggable streams.
*
*
This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.
@@ -52,19 +53,19 @@ class FactoryFinder {
/**
* Cache for properties in java.home/lib/jaxp.properties
*/
- static Properties cacheProps = new Properties();
+ final private static Properties cacheProps = new Properties();
/**
* Flag indicating if properties from java.home/lib/jaxp.properties
* have been cached.
*/
- static volatile boolean firstTime = true;
+ private static volatile boolean firstTime = true;
/**
* Security support class use to check access control before
* getting certain system resources.
*/
- static SecuritySupport ss = new SecuritySupport();
+ final private static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output
static {
@@ -103,25 +104,25 @@ class FactoryFinder {
try {
if (cl == null) {
if (useBSClsLoader) {
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
}
else {
throw e1;
@@ -133,6 +134,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -142,16 +146,19 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback)
+ throws FactoryConfigurationError
{
- return newInstance(className, cl, doFallback, false);
+ return newInstance(type, className, cl, doFallback, false);
}
/**
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
+ *
* @param className Name of the concrete class corresponding to the
* service provider
*
@@ -164,9 +171,12 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl,
+ boolean doFallback, boolean useBSClsLoader)
+ throws FactoryConfigurationError
{
+ assert type != null;
+
// make sure we have access to restricted packages
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@@ -176,20 +186,23 @@ class FactoryFinder {
}
try {
- Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ Class> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ if (!type.isAssignableFrom(providerClass)) {
+ throw new ClassCastException(className + " cannot be cast to " + type.getName());
+ }
Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl);
}
- return instance;
+ return type.cast(instance);
}
catch (ClassNotFoundException x) {
- throw new ConfigurationError(
+ throw new FactoryConfigurationError(
"Provider " + className + " not found", x);
}
catch (Exception x) {
- throw new ConfigurationError(
+ throw new FactoryConfigurationError(
"Provider " + className + " could not be instantiated: " + x,
x);
}
@@ -200,17 +213,18 @@ class FactoryFinder {
*
* @return Class object of factory, never null
*
- * @param factoryId Name of the factory to find, same as
- * a property name
+ * @param type Base class / Service interface of the
+ * factory to find.
+ *
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
*
* Package private so this code can be shared.
*/
- static Object find(String factoryId, String fallbackClassName)
- throws ConfigurationError
+ static T find(Class type, String fallbackClassName)
+ throws FactoryConfigurationError
{
- return find(factoryId, null, fallbackClassName);
+ return find(type, type.getName(), null, fallbackClassName);
}
/**
@@ -218,6 +232,9 @@ class FactoryFinder {
* entry point.
* @return Class object of factory, never null
*
+ * @param type Base class / Service interface of the
+ * factory to find.
+ *
* @param factoryId Name of the factory to find, same as
* a property name
*
@@ -229,8 +246,8 @@ class FactoryFinder {
*
* Package private so this code can be shared.
*/
- static Object find(String factoryId, ClassLoader cl, String fallbackClassName)
- throws ConfigurationError
+ static T find(Class type, String factoryId, ClassLoader cl, String fallbackClassName)
+ throws FactoryConfigurationError
{
dPrint("find factoryId =" + factoryId);
@@ -239,7 +256,9 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint("found system property, value=" + systemProp);
- return newInstance(systemProp, null, true);
+ // There's a bug here - because 'cl' is ignored.
+ // This will be handled separately.
+ return newInstance(type, systemProp, null, true);
}
}
catch (SecurityException se) {
@@ -250,7 +269,6 @@ class FactoryFinder {
// $java.home/lib/jaxp.properties if former not present
String configFile = null;
try {
- String factoryClassName = null;
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
@@ -269,130 +287,80 @@ class FactoryFinder {
if (ss.doesFileExist(f)) {
dPrint("Read properties file "+f);
cacheProps.load(ss.getFileInputStream(f));
+ }
+ }
}
}
}
- }
- }
- factoryClassName = cacheProps.getProperty(factoryId);
+ final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
dPrint("found in " + configFile + " value=" + factoryClassName);
- return newInstance(factoryClassName, null, true);
+ // There's a bug here - because 'cl' is ignored.
+ // This will be handled separately.
+ return newInstance(type, factoryClassName, null, true);
}
}
catch (Exception ex) {
if (debug) ex.printStackTrace();
}
- // Try Jar Service Provider Mechanism
- Object provider = findJarServiceProvider(factoryId);
- if (provider != null) {
- return provider;
+ if (type.getName().equals(factoryId)) {
+ // Try Jar Service Provider Mechanism
+ final T provider = findServiceProvider(type);
+ if (provider != null) {
+ return provider;
+ }
+ } else {
+ // We're in the case where a 'custom' factoryId was provided,
+ // and in every case where that happens, we expect that
+ // fallbackClassName will be null.
+ assert fallbackClassName == null;
}
if (fallbackClassName == null) {
- throw new ConfigurationError(
+ throw new FactoryConfigurationError(
"Provider for " + factoryId + " cannot be found", null);
}
dPrint("loaded from fallback value: " + fallbackClassName);
- return newInstance(fallbackClassName, cl, true);
+ return newInstance(type, fallbackClassName, cl, true);
}
/*
- * Try to find provider using Jar Service Provider Mechanism
+ * Try to find provider using the ServiceLoader API
+ *
+ * @param type Base class / Service interface of the factory to find.
*
* @return instance of provider class if found or null
*/
- private static Object findJarServiceProvider(String factoryId)
- throws ConfigurationError
- {
- String serviceId = "META-INF/services/" + factoryId;
- InputStream is = null;
-
- // First try the Context ClassLoader
- ClassLoader cl = ss.getContextClassLoader();
- boolean useBSClsLoader = false;
- if (cl != null) {
- is = ss.getResourceAsStream(cl, serviceId);
-
- // If no provider found then try the current ClassLoader
- if (is == null) {
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
- } else {
- // No Context ClassLoader, try the current ClassLoader
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
-
- if (is == null) {
- // No provider found
- return null;
- }
-
- if (debug) { // Extra check to avoid computing cl strings
- dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
- }
-
- BufferedReader rd;
+ private static T findServiceProvider(final Class type) {
try {
- rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- }
- catch (java.io.UnsupportedEncodingException e) {
- rd = new BufferedReader(new InputStreamReader(is));
- }
-
- String factoryClassName = null;
- try {
- // XXX Does not handle all possible input as specified by the
- // Jar Service Provider specification
- factoryClassName = rd.readLine();
- rd.close();
- } catch (IOException x) {
- // No provider found
- return null;
- }
-
- if (factoryClassName != null && !"".equals(factoryClassName)) {
- dPrint("found in resource, value=" + factoryClassName);
-
- // Note: here we do not want to fall back to the current
- // ClassLoader because we want to avoid the case where the
- // resource file was found using one ClassLoader and the
- // provider class was instantiated using a different one.
- return newInstance(factoryClassName, cl, false, useBSClsLoader);
- }
-
- // No provider found
- return null;
- }
-
- static class ConfigurationError extends Error {
- private Exception exception;
-
- /**
- * Construct a new instance with the specified detail string and
- * exception.
- */
- ConfigurationError(String msg, Exception x) {
- super(msg);
- this.exception = x;
- }
-
- Exception getException() {
- return exception;
- }
- /**
- * use the exception chaining mechanism of JDK1.4
- */
- @Override
- public Throwable getCause() {
- return exception;
- }
- }
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public T run() {
+ final ServiceLoader serviceLoader = ServiceLoader.load(type);
+ final Iterator iterator = serviceLoader.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return null;
+ }
+ }
+ });
+ } catch(ServiceConfigurationError e) {
+ // It is not possible to wrap an error directly in
+ // FactoryConfigurationError - so we need to wrap the
+ // ServiceConfigurationError in a RuntimeException.
+ // The alternative would be to modify the logic in
+ // FactoryConfigurationError to allow setting a
+ // Throwable as the cause, but that could cause
+ // compatibility issues down the road.
+ final RuntimeException x = new RuntimeException(
+ "Provider for " + type + " cannot be created", e);
+ final FactoryConfigurationError error =
+ new FactoryConfigurationError(x, x.getMessage());
+ throw error;
+ }
+ }
}
diff --git a/jaxp/src/javax/xml/stream/XMLEventFactory.java b/jaxp/src/javax/xml/stream/XMLEventFactory.java
index f92d77805f7..d9e47ef0727 100644
--- a/jaxp/src/javax/xml/stream/XMLEventFactory.java
+++ b/jaxp/src/javax/xml/stream/XMLEventFactory.java
@@ -23,14 +23,14 @@
*/
/*
- * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved.
+ * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/
package javax.xml.stream;
-import javax.xml.stream.events.*;
+import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
-import java.util.Iterator;
+import javax.xml.stream.events.*;
/**
* This interface defines a utility class for creating instances of
* XMLEvents
@@ -54,48 +54,59 @@ public abstract class XMLEventFactory {
/**
- * Create a new instance of the factory
+ * Creates a new instance of the factory in exactly the same manner as the
+ * {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/
public static XMLEventFactory newInstance()
throws FactoryConfigurationError
{
- return (XMLEventFactory) FactoryFinder.find(
- JAXPFACTORYID,
- DEFAULIMPL);
+ return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL);
}
/**
* Create a new instance of the factory.
+ *
* This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine
* the XMLEventFactory implementation class to load:
+ *
+ *
+ *
* Use the javax.xml.stream.XMLEventFactory system property.
+ *
+ *
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the system property defined above.
- * Use the Services API (as detailed in the JAR specification), if available,
- * to determine the classname. The Services API will look for a classname
- * in the file META-INF/services/javax.xml.stream.XMLEventFactory in jars
- * available to the runtime.
- * Platform default XMLEventFactory instance.
- *
+ *
+ *
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, the system-default implementation is returned.
+ *
+ *
+ *
* Once an application has obtained a reference to a XMLEventFactory it
* can use the factory to configure and obtain stream instances.
- *
+ *
+ *
* Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to
* the deprecated method.
- *
- * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
+ *
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static XMLEventFactory newFactory()
throws FactoryConfigurationError
{
- return (XMLEventFactory) FactoryFinder.find(
- JAXPFACTORYID,
- DEFAULIMPL);
+ return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL);
}
/**
@@ -116,40 +127,59 @@ public abstract class XMLEventFactory {
public static XMLEventFactory newInstance(String factoryId,
ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null);
}
/**
* Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used.
+ *
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLEventFactory implementation class to load:
+ *
+ *
+ *
+ * Use the value of the system property identified by {@code factoryId}.
+ *
+ *
+ * Use the properties file "lib/stax.properties" in the JRE directory.
+ * This configuration file is in standard java.util.Properties format
+ * and contains the fully qualified name of the implementation class
+ * with the key being the given {@code factoryId}.
+ *
+ *
+ * If {@code factoryId} is "javax.xml.stream.XMLEventFactory",
+ * use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ *
+ *
*
+ *
* Note that this is a new method that replaces the deprecated
- * newInstance(String factoryId, ClassLoader classLoader) method.
+ * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
+ * newInstance(String factoryId, ClassLoader classLoader)} method.
* No changes in behavior are defined by this replacement method relative
* to the deprecated method.
+ *
*
* @param factoryId Name of the factory to find, same as
* a property name
* @param classLoader classLoader to use
* @return the factory implementation
- * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static XMLEventFactory newFactory(String factoryId,
- ClassLoader classLoader)
+ ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null);
}
/**
diff --git a/jaxp/src/javax/xml/stream/XMLInputFactory.java b/jaxp/src/javax/xml/stream/XMLInputFactory.java
index 48dc3675fa5..2bfbad5d461 100644
--- a/jaxp/src/javax/xml/stream/XMLInputFactory.java
+++ b/jaxp/src/javax/xml/stream/XMLInputFactory.java
@@ -23,13 +23,13 @@
*/
/*
- * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved.
+ * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/
package javax.xml.stream;
-import javax.xml.transform.Source;
import javax.xml.stream.util.XMLEventAllocator;
+import javax.xml.transform.Source;
/**
* Defines an abstract implementation of a factory for getting streams.
@@ -144,48 +144,59 @@ public abstract class XMLInputFactory {
protected XMLInputFactory(){}
/**
- * Create a new instance of the factory.
+ * Creates a new instance of the factory in exactly the same manner as the
+ * {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/
public static XMLInputFactory newInstance()
throws FactoryConfigurationError
{
- return (XMLInputFactory) FactoryFinder.find(
- "javax.xml.stream.XMLInputFactory",
- DEFAULIMPL);
+ return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL);
}
/**
* Create a new instance of the factory.
+ *
* This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine
* the XMLInputFactory implementation class to load:
+ *
+ *
+ *
* Use the javax.xml.stream.XMLInputFactory system property.
+ *
+ *
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the system property defined above.
- * Use the Services API (as detailed in the JAR specification), if available,
- * to determine the classname. The Services API will look for a classname
- * in the file META-INF/services/javax.xml.stream.XMLInputFactory in jars
- * available to the runtime.
- * Platform default XMLInputFactory instance.
- *
+ *
+ *
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, the system-default implementation is returned.
+ *
+ *
+ *
* Once an application has obtained a reference to a XMLInputFactory it
* can use the factory to configure and obtain stream instances.
- *
+ *
+ *
* Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to
* the deprecated method.
- *
- * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
+ *
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static XMLInputFactory newFactory()
throws FactoryConfigurationError
{
- return (XMLInputFactory) FactoryFinder.find(
- "javax.xml.stream.XMLInputFactory",
- DEFAULIMPL);
+ return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL);
}
/**
@@ -206,40 +217,60 @@ public abstract class XMLInputFactory {
public static XMLInputFactory newInstance(String factoryId,
ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
}
/**
* Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used.
+ *
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLInputFactory implementation class to load:
+ *
+ *
+ *
+ * Use the value of the system property identified by {@code factoryId}.
+ *
+ *
+ * Use the properties file "lib/stax.properties" in the JRE directory.
+ * This configuration file is in standard java.util.Properties format
+ * and contains the fully qualified name of the implementation class
+ * with the key being the given {@code factoryId}.
+ *
+ *
+ * If {@code factoryId} is "javax.xml.stream.XMLInputFactory",
+ * use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ *
+ *
*
+ *
* Note that this is a new method that replaces the deprecated
- * newInstance(String factoryId, ClassLoader classLoader) method.
+ * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
+ * newInstance(String factoryId, ClassLoader classLoader)} method.
* No changes in behavior are defined by this replacement method relative
* to the deprecated method.
+ *
*
* @param factoryId Name of the factory to find, same as
* a property name
* @param classLoader classLoader to use
* @return the factory implementation
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/
public static XMLInputFactory newFactory(String factoryId,
ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
}
/**
diff --git a/jaxp/src/javax/xml/stream/XMLOutputFactory.java b/jaxp/src/javax/xml/stream/XMLOutputFactory.java
index b0d4f0b309f..a5a593c9cd0 100644
--- a/jaxp/src/javax/xml/stream/XMLOutputFactory.java
+++ b/jaxp/src/javax/xml/stream/XMLOutputFactory.java
@@ -23,7 +23,7 @@
*/
/*
- * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved.
+ * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/
package javax.xml.stream;
@@ -120,46 +120,58 @@ public abstract class XMLOutputFactory {
protected XMLOutputFactory(){}
/**
- * Create a new instance of the factory.
+ * Creates a new instance of the factory in exactly the same manner as the
+ * {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/
public static XMLOutputFactory newInstance()
throws FactoryConfigurationError
{
- return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory",
- DEFAULIMPL);
+ return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL);
}
/**
* Create a new instance of the factory.
+ *
* This static method creates a new factory instance. This method uses the
* following ordered lookup procedure to determine the XMLOutputFactory
* implementation class to load:
+ *
+ *
+ *
* Use the javax.xml.stream.XMLOutputFactory system property.
+ *
+ *
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the system property defined above.
- * Use the Services API (as detailed in the JAR specification), if available,
- * to determine the classname. The Services API will look for a classname
- * in the file META-INF/services/javax.xml.stream.XMLOutputFactory in jars
- * available to the runtime.
- * Platform default XMLOutputFactory instance.
- *
+ *
+ *
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, the system-default implementation is returned.
+ *
+ *
* Once an application has obtained a reference to a XMLOutputFactory it
* can use the factory to configure and obtain stream instances.
- *
+ *
+ *
* Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to the
* deprecated method.
- *
- * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
+ *
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static XMLOutputFactory newFactory()
throws FactoryConfigurationError
{
- return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory",
- DEFAULIMPL);
+ return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL);
}
/**
@@ -179,42 +191,59 @@ public abstract class XMLOutputFactory {
public static XMLInputFactory newInstance(String factoryId,
ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
}
/**
* Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used.
+ *
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLOutputFactory implementation class to load:
+ *
+ *
+ *
+ * Use the value of the system property identified by {@code factoryId}.
+ *
+ *
+ * Use the properties file "lib/stax.properties" in the JRE directory.
+ * This configuration file is in standard java.util.Properties format
+ * and contains the fully qualified name of the implementation class
+ * with the key being the given {@code factoryId}.
+ *
+ *
+ * If {@code factoryId} is "javax.xml.stream.XMLOutputFactory",
+ * use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ *
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ *
+ *
*
+ *
* Note that this is a new method that replaces the deprecated
- * newInstance(String factoryId, ClassLoader classLoader) method.
- *
- * No changes in behavior are defined by this replacement method relative
- * to the deprecated method.
- *
+ * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
+ * newInstance(String factoryId, ClassLoader classLoader)} method.
+ * No changes in behavior are defined by this replacement method relative
+ * to the deprecated method.
+ *
*
* @param factoryId Name of the factory to find, same as
* a property name
* @param classLoader classLoader to use
* @return the factory implementation
- * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
+ * @throws FactoryConfigurationError in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static XMLOutputFactory newFactory(String factoryId,
ClassLoader classLoader)
throws FactoryConfigurationError {
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (XMLOutputFactory) FactoryFinder.find(factoryId, classLoader, null);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new FactoryConfigurationError(e.getException(),
- e.getMessage());
- }
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.find(XMLOutputFactory.class, factoryId, classLoader, null);
}
/**
diff --git a/jaxp/src/javax/xml/transform/FactoryFinder.java b/jaxp/src/javax/xml/transform/FactoryFinder.java
index 63cc8cd3f22..4ae6568aa57 100644
--- a/jaxp/src/javax/xml/transform/FactoryFinder.java
+++ b/jaxp/src/javax/xml/transform/FactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -25,13 +25,15 @@
package javax.xml.transform;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
*
Implements pluggable Datatypes.
@@ -53,7 +55,7 @@ class FactoryFinder {
/**
* Cache for properties in java.home/lib/jaxp.properties
*/
- static Properties cacheProps = new Properties();
+ private final static Properties cacheProps = new Properties();
/**
* Flag indicating if properties from java.home/lib/jaxp.properties
@@ -65,7 +67,7 @@ class FactoryFinder {
* Security support class use to check access control before
* getting certain system resources.
*/
- static SecuritySupport ss = new SecuritySupport();
+ private final static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output
static {
@@ -98,31 +100,31 @@ class FactoryFinder {
*
* Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/
- static private Class getProviderClass(String className, ClassLoader cl,
+ static private Class> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
try {
if (cl == null) {
if (useBSClsLoader) {
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
}
else {
- return cl.loadClass(className);
+ return Class.forName(className, false, cl);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
+ return Class.forName(className, false, FactoryFinder.class.getClassLoader());
}
else {
throw e1;
@@ -134,24 +136,8 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method
* getProviderClass() in order to load the class.
*
- * @param className Name of the concrete class corresponding to the
- * service provider
- *
- * @param cl ClassLoader used to load the factory class. If null
- * current Thread's context classLoader is used to load the factory class.
- *
- * @param doFallback True if the current ClassLoader should be tried as
- * a fallback if the class is not found using cl
- */
- static Object newInstance(String className, ClassLoader cl, boolean doFallback)
- throws ConfigurationError
- {
- return newInstance(className, cl, doFallback, false, false);
- }
-
- /**
- * Create an instance of a class. Delegates to method
- * getProviderClass() in order to load the class.
+ * @param type Base class / Service interface of the factory to
+ * instantiate.
*
* @param className Name of the concrete class corresponding to the
* service provider
@@ -162,14 +148,15 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl
*
- * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
- * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
- *
* @param useServicesMechanism True use services mechanism
*/
- static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader, boolean useServicesMechanism)
- throws ConfigurationError
+ static T newInstance(Class type, String className, ClassLoader cl,
+ boolean doFallback, boolean useServicesMechanism)
+ throws TransformerFactoryConfigurationError
{
+ assert type != null;
+
+ boolean useBSClsLoader = false;
// make sure we have access to restricted packages
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@@ -179,10 +166,13 @@ class FactoryFinder {
}
try {
- Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ Class> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
+ if (!type.isAssignableFrom(providerClass)) {
+ throw new ClassCastException(className + " cannot be cast to " + type.getName());
+ }
Object instance = null;
if (!useServicesMechanism) {
- instance = newInstanceNoServiceLoader(providerClass);
+ instance = newInstanceNoServiceLoader(type, providerClass);
}
if (instance == null) {
instance = providerClass.newInstance();
@@ -191,63 +181,87 @@ class FactoryFinder {
dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl);
}
- return instance;
+ return type.cast(instance);
}
catch (ClassNotFoundException x) {
- throw new ConfigurationError(
- "Provider " + className + " not found", x);
+ throw new TransformerFactoryConfigurationError(x,
+ "Provider " + className + " not found");
}
catch (Exception x) {
- throw new ConfigurationError(
- "Provider " + className + " could not be instantiated: " + x,
- x);
+ throw new TransformerFactoryConfigurationError(x,
+ "Provider " + className + " could not be instantiated: " + x);
}
}
+
/**
* Try to construct using newTransformerFactoryNoServiceLoader
* method if available.
*/
- private static Object newInstanceNoServiceLoader(
- Class> providerClass
- ) {
+ private static T newInstanceNoServiceLoader(Class type, Class> providerClass) {
// Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) {
return null;
}
try {
- Method creationMethod =
- providerClass.getDeclaredMethod(
- "newTransformerFactoryNoServiceLoader"
- );
- return creationMethod.invoke(null, (Object[])null);
- } catch (NoSuchMethodException exc) {
- return null;
- } catch (Exception exc) {
+ final Method creationMethod =
+ providerClass.getDeclaredMethod(
+ "newTransformerFactoryNoServiceLoader"
+ );
+ final int modifiers = creationMethod.getModifiers();
+
+ // Do not call the method if it's not public static.
+ if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) {
return null;
+ }
+
+ // Only call the method if it's declared to return an instance of
+ // TransformerFactory
+ final Class> returnType = creationMethod.getReturnType();
+ if (type.isAssignableFrom(returnType)) {
+ final Object result = creationMethod.invoke(null, (Object[])null);
+ return type.cast(result);
+ } else {
+ // This should not happen, as
+ // TransformerFactoryImpl.newTransformerFactoryNoServiceLoader is
+ // declared to return TransformerFactory.
+ throw new ClassCastException(returnType + " cannot be cast to " + type);
+ }
+ } catch (ClassCastException e) {
+ throw new TransformerFactoryConfigurationError(e, e.getMessage());
+ } catch (NoSuchMethodException exc) {
+ return null;
+ } catch (Exception exc) {
+ return null;
}
}
+
/**
* Finds the implementation Class object in the specified order. Main
* entry point.
* @return Class object of factory, never null
*
- * @param factoryId Name of the factory to find, same as
- * a property name
+ * @param type Base class / Service interface of the
+ * factory to find.
+ *
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
*
* Package private so this code can be shared.
*/
- static Object find(String factoryId, String fallbackClassName)
- throws ConfigurationError
+ static T find(Class type, String fallbackClassName)
+ throws TransformerFactoryConfigurationError
{
+ assert type != null;
+
+ final String factoryId = type.getName();
+
dPrint("find factoryId =" + factoryId);
// Use the system property first
try {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint("found system property, value=" + systemProp);
- return newInstance(systemProp, null, true, false, true);
+ return newInstance(type, systemProp, null, true, true);
}
}
catch (SecurityException se) {
@@ -256,7 +270,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties
try {
- String factoryClassName = null;
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
@@ -271,11 +284,11 @@ class FactoryFinder {
}
}
}
- factoryClassName = cacheProps.getProperty(factoryId);
+ final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
- return newInstance(factoryClassName, null, true, false, true);
+ return newInstance(type, factoryClassName, null, true, true);
}
}
catch (Exception ex) {
@@ -283,113 +296,54 @@ class FactoryFinder {
}
// Try Jar Service Provider Mechanism
- Object provider = findJarServiceProvider(factoryId);
+ T provider = findServiceProvider(type);
if (provider != null) {
return provider;
}
if (fallbackClassName == null) {
- throw new ConfigurationError(
- "Provider for " + factoryId + " cannot be found", null);
+ throw new TransformerFactoryConfigurationError(null,
+ "Provider for " + factoryId + " cannot be found");
}
dPrint("loaded from fallback value: " + fallbackClassName);
- return newInstance(fallbackClassName, null, true, false, true);
+ return newInstance(type, fallbackClassName, null, true, true);
}
/*
- * Try to find provider using Jar Service Provider Mechanism
+ * Try to find provider using the ServiceLoader.
+ *
+ * @param type Base class / Service interface of the factory to find.
*
* @return instance of provider class if found or null
*/
- private static Object findJarServiceProvider(String factoryId)
- throws ConfigurationError
+ private static T findServiceProvider(final Class type)
+ throws TransformerFactoryConfigurationError
{
- String serviceId = "META-INF/services/" + factoryId;
- InputStream is = null;
-
- // First try the Context ClassLoader
- ClassLoader cl = ss.getContextClassLoader();
- boolean useBSClsLoader = false;
- if (cl != null) {
- is = ss.getResourceAsStream(cl, serviceId);
-
- // If no provider found then try the current ClassLoader
- if (is == null) {
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
- } else {
- // No Context ClassLoader, try the current ClassLoader
- cl = FactoryFinder.class.getClassLoader();
- is = ss.getResourceAsStream(cl, serviceId);
- useBSClsLoader = true;
- }
-
- if (is == null) {
- // No provider found
- return null;
- }
-
- if (debug) { // Extra check to avoid computing cl strings
- dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
- }
-
- BufferedReader rd;
- try {
- rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- }
- catch (java.io.UnsupportedEncodingException e) {
- rd = new BufferedReader(new InputStreamReader(is));
- }
-
- String factoryClassName = null;
- try {
- // XXX Does not handle all possible input as specified by the
- // Jar Service Provider specification
- factoryClassName = rd.readLine();
- rd.close();
- } catch (IOException x) {
- // No provider found
- return null;
- }
-
- if (factoryClassName != null && !"".equals(factoryClassName)) {
- dPrint("found in resource, value=" + factoryClassName);
-
- // Note: here we do not want to fall back to the current
- // ClassLoader because we want to avoid the case where the
- // resource file was found using one ClassLoader and the
- // provider class was instantiated using a different one.
- return newInstance(factoryClassName, cl, false, useBSClsLoader, true);
- }
-
- // No provider found
- return null;
- }
-
- static class ConfigurationError extends Error {
- private Exception exception;
-
- /**
- * Construct a new instance with the specified detail string and
- * exception.
- */
- ConfigurationError(String msg, Exception x) {
- super(msg);
- this.exception = x;
- }
-
- Exception getException() {
- return exception;
- }
- /**
- * use the exception chaining mechanism of JDK1.4
- */
- @Override
- public Throwable getCause() {
- return exception;
+ try {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public T run() {
+ final ServiceLoader serviceLoader = ServiceLoader.load(type);
+ final Iterator iterator = serviceLoader.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return null;
+ }
+ }
+ });
+ } catch(ServiceConfigurationError e) {
+ // It is not possible to wrap an error directly in
+ // FactoryConfigurationError - so we need to wrap the
+ // ServiceConfigurationError in a RuntimeException.
+ // The alternative would be to modify the logic in
+ // FactoryConfigurationError to allow setting a
+ // Throwable as the cause, but that could cause
+ // compatibility issues down the road.
+ final RuntimeException x = new RuntimeException(
+ "Provider for " + type + " cannot be created", e);
+ final TransformerFactoryConfigurationError error =
+ new TransformerFactoryConfigurationError(x, x.getMessage());
+ throw error;
}
}
-
}
diff --git a/jaxp/src/javax/xml/transform/TransformerFactory.java b/jaxp/src/javax/xml/transform/TransformerFactory.java
index 741740cf7aa..61528855be6 100644
--- a/jaxp/src/javax/xml/transform/TransformerFactory.java
+++ b/jaxp/src/javax/xml/transform/TransformerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2013, 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
@@ -52,8 +52,8 @@ public abstract class TransformerFactory {
/**
*
Obtain a new instance of a TransformerFactory.
- * This static method creates a new factory instance
- * This method uses the following ordered lookup procedure to determine
+ * This static method creates a new factory instance.
+ *
This method uses the following ordered lookup procedure to determine
* the TransformerFactory implementation class to
* load:
*
@@ -67,7 +67,7 @@ public abstract class TransformerFactory {
* format and contains the fully qualified name of the
* implementation class with the key being the system property defined
* above.
- *
+ *
* The jaxp.properties file is read only once by the JAXP implementation
* and it's values are then cached for future use. If the file does not exist
* when the first attempt is made to read from it, no further attempts are
@@ -75,14 +75,12 @@ public abstract class TransformerFactory {
* of any property in jaxp.properties after it has been read for the first time.
*
*
- * Use the Services API (as detailed in the JAR specification), if
- * available, to determine the classname. The Services API will look
- * for a classname in the file
- * META-INF/services/javax.xml.transform.TransformerFactory
- * in jars available to the runtime.
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
*
*
- * Platform default TransformerFactory instance.
+ * Otherwise, the system-default implementation is returned.
*
*
*
@@ -92,22 +90,18 @@ public abstract class TransformerFactory {
*
* @return new TransformerFactory instance, never null.
*
- * @throws TransformerFactoryConfigurationError Thrown if the implementation
- * is not available or cannot be instantiated.
+ * @throws TransformerFactoryConfigurationError Thrown in case of {@linkplain
+ * java.util.ServiceConfigurationError service configuration error} or if
+ * the implementation is not available or cannot be instantiated.
*/
public static TransformerFactory newInstance()
throws TransformerFactoryConfigurationError {
- try {
- return (TransformerFactory) FactoryFinder.find(
+
+ return FactoryFinder.find(
/* The default property name according to the JAXP spec */
- "javax.xml.transform.TransformerFactory",
+ TransformerFactory.class,
/* The fallback implementation class name, XSLTC */
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
- } catch (FactoryFinder.ConfigurationError e) {
- throw new TransformerFactoryConfigurationError(
- e.getException(),
- e.getMessage());
- }
}
/**
@@ -147,14 +141,10 @@ public abstract class TransformerFactory {
*/
public static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader)
throws TransformerFactoryConfigurationError{
- try {
- //do not fallback if given classloader can't find the class, throw exception
- return (TransformerFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false);
- } catch (FactoryFinder.ConfigurationError e) {
- throw new TransformerFactoryConfigurationError(
- e.getException(),
- e.getMessage());
- }
+
+ //do not fallback if given classloader can't find the class, throw exception
+ return FactoryFinder.newInstance(TransformerFactory.class,
+ factoryClassName, classLoader, false, false);
}
/**
*
Process the Source into a Transformer
diff --git a/jaxp/src/javax/xml/validation/SchemaFactory.java b/jaxp/src/javax/xml/validation/SchemaFactory.java
index 97b31654069..6f156af0ff5 100644
--- a/jaxp/src/javax/xml/validation/SchemaFactory.java
+++ b/jaxp/src/javax/xml/validation/SchemaFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -27,15 +27,14 @@ package javax.xml.validation;
import java.io.File;
import java.net.URL;
-
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
-
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
/**
* Factory that creates {@link Schema} objects. Entry-point to
@@ -79,7 +78,7 @@ import org.xml.sax.SAXNotSupportedException;
* and has a significant effect on the parsing process, it is impossible
* to define the DTD validation as a process independent from parsing.
* For this reason, this specification does not define the semantics for
- * the XML DTD. This doesn't prohibit implentors from implementing it
+ * the XML DTD. This doesn't prohibit implementors from implementing it
* in a way they see fit, but users are warned that any DTD
* validation implemented on this interface necessarily deviate from
* the XML DTD semantics as defined in the XML 1.0.
@@ -147,14 +146,17 @@ public abstract class SchemaFactory {
* is looked for. If present, the value is processed just like above.
*
*
- *
The class loader is asked for service provider provider-configuration files matching
- * javax.xml.validation.SchemaFactory in the resource directory META-INF/services.
- * See the JAR File Specification for file format and parsing rules.
- * Each potential service provider is required to implement the method:
- * The first service provider found in class loader order that supports the specified schema language is returned.
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ * Each potential service provider is required to implement the method
+ * {@link #isSchemaLanguageSupported(String schemaLanguage)}.
+ *
+ * The first service provider found that supports the specified schema
+ * language is returned.
+ *
+ * In case of {@link java.util.ServiceConfigurationError} a
+ * {@link SchemaFactoryConfigurationError} will be thrown.
*
*
* Platform default SchemaFactory is located
@@ -186,10 +188,12 @@ public abstract class SchemaFactory {
* If no implementation of the schema language is available.
* @throws NullPointerException
* If the schemaLanguage parameter is null.
+ * @throws SchemaFactoryConfigurationError
+ * If a configuration error is encountered.
*
* @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader)
*/
- public static final SchemaFactory newInstance(String schemaLanguage) {
+ public static SchemaFactory newInstance(String schemaLanguage) {
ClassLoader cl;
cl = ss.getContextClassLoader();
@@ -275,19 +279,19 @@ public abstract class SchemaFactory {
}
- /**
- *
Is specified schema supported by this SchemaFactory?
- *
- * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand.
+ /**
+ *
Is specified schema supported by this SchemaFactory?
+ *
+ * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand.
* schemaLanguage must specify a valid schema language.
- *
- * @return true if SchemaFactory supports schemaLanguage, else false.
- *
- * @throws NullPointerException If schemaLanguage is null.
- * @throws IllegalArgumentException If schemaLanguage.length() == 0
- * or schemaLanguage does not specify a valid schema language.
- */
- public abstract boolean isSchemaLanguageSupported(String schemaLanguage);
+ *
+ * @return true if SchemaFactory supports schemaLanguage, else false.
+ *
+ * @throws NullPointerException If schemaLanguage is null.
+ * @throws IllegalArgumentException If schemaLanguage.length() == 0
+ * or schemaLanguage does not specify a valid schema language.
+ */
+ public abstract boolean isSchemaLanguageSupported(String schemaLanguage);
/**
* Look up the value of a feature flag.
diff --git a/jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java b/jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java
new file mode 100644
index 00000000000..47c4dda1ee6
--- /dev/null
+++ b/jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+package javax.xml.validation;
+
+/**
+ * Thrown when a problem with configuration with the Schema Factories
+ * exists. This error will typically be thrown when the class of a
+ * schema factory specified in the system properties cannot be found
+ * or instantiated.
+ * @since 1.8
+ */
+public final class SchemaFactoryConfigurationError extends Error {
+
+ static final long serialVersionUID = 3531438703147750126L;
+
+ /**
+ * Create a new SchemaFactoryConfigurationError with no
+ * detail message.
+ */
+ public SchemaFactoryConfigurationError() {
+ }
+
+
+ /**
+ * Create a new SchemaFactoryConfigurationError with
+ * the String specified as an error message.
+ *
+ * @param message The error message for the exception.
+ */
+ public SchemaFactoryConfigurationError(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new SchemaFactoryConfigurationError with the
+ * given Throwable base cause.
+ *
+ * @param cause The exception or error to be encapsulated in a
+ * SchemaFactoryConfigurationError.
+ */
+ public SchemaFactoryConfigurationError(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a new SchemaFactoryConfigurationError with the
+ * given Throwable base cause and detail message.
+ *
+ * @param cause The exception or error to be encapsulated in a
+ * SchemaFactoryConfigurationError.
+ * @param message The detail message.
+ */
+ public SchemaFactoryConfigurationError(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java b/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java
index 6ae593a105c..907e67a66fa 100644
--- a/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java
+++ b/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -25,19 +25,16 @@
package javax.xml.validation;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
* Implementation of {@link SchemaFactory#newInstance(String)}.
@@ -53,17 +50,17 @@ class SchemaFactoryFinder {
/**
*
Take care of restrictions imposed by java security model
*/
- private static SecuritySupport ss = new SecuritySupport();
+ private static final SecuritySupport ss = new SecuritySupport();
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal";
/**
*
Cache properties for performance.
*/
- private static Properties cacheProps = new Properties();
+ private static final Properties cacheProps = new Properties();
- /**
- *
+ */
+ private static volatile boolean firstTime = true;
static {
// Use try/catch block to support applets
@@ -115,7 +112,7 @@ class SchemaFactoryFinder {
return;
}
} catch( Throwable unused ) {
- ; // getContextClassLoader() undefined in JDK1.1
+ // getContextClassLoader() undefined in JDK1.1
}
if( classLoader==ClassLoader.getSystemClassLoader() ) {
@@ -138,9 +135,13 @@ class SchemaFactoryFinder {
*
* @throws NullPointerException
* If the schemaLanguage parameter is null.
+ * @throws SchemaFactoryConfigurationError
+ * If a configuration error is encountered.
*/
public SchemaFactory newFactory(String schemaLanguage) {
- if(schemaLanguage==null) throw new NullPointerException();
+ if(schemaLanguage==null) {
+ throw new NullPointerException();
+ }
SchemaFactory f = _newFactory(schemaLanguage);
if (f != null) {
debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);
@@ -183,7 +184,6 @@ class SchemaFactoryFinder {
String configFile = javah + File.separator +
"lib" + File.separator + "jaxp.properties";
- String factoryClassName = null ;
// try to read from $java.home/lib/jaxp.properties
try {
@@ -199,7 +199,7 @@ class SchemaFactoryFinder {
}
}
}
- factoryClassName = cacheProps.getProperty(propertyName);
+ final String factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
@@ -214,21 +214,15 @@ class SchemaFactoryFinder {
}
}
- // try META-INF/services files
- Iterator sitr = createServiceFileIterator();
- while(sitr.hasNext()) {
- URL resource = (URL)sitr.next();
- debugPrintln("looking into " + resource);
- try {
- sf = loadFromService(schemaLanguage,resource.toExternalForm(),
- ss.getURLInputStream(resource));
- if(sf!=null) return sf;
- } catch(IOException e) {
- if( debug ) {
- debugPrintln("failed to read "+resource);
- e.printStackTrace();
- }
- }
+ // Try with ServiceLoader
+ final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage);
+
+ // The following assertion should always be true.
+ // Uncomment it, recompile, and run with -ea in case of doubts:
+ // assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage);
+
+ if (factoryImpl != null) {
+ return factoryImpl;
}
// platform default
@@ -246,8 +240,8 @@ class SchemaFactoryFinder {
* @param className Name of class to create.
* @return Created class or null.
*/
- private Class createClass(String className) {
- Class clazz;
+ private Class> createClass(String className) {
+ Class> clazz;
// make sure we have access to restricted packages
boolean internal = false;
if (System.getSecurityManager() != null) {
@@ -256,25 +250,27 @@ class SchemaFactoryFinder {
}
}
- try {
- if (classLoader != null && !internal) {
- clazz = classLoader.loadClass(className);
- } else {
- clazz = Class.forName(className);
- }
- } catch (Throwable t) {
- if(debug) t.printStackTrace();
- return null;
+ try {
+ if (classLoader != null && !internal) {
+ clazz = Class.forName(className, false, classLoader);
+ } else {
+ clazz = Class.forName(className);
}
+ } catch (Throwable t) {
+ if(debug) {
+ t.printStackTrace();
+ }
+ return null;
+ }
- return clazz;
+ return clazz;
}
/**
*
Creates an instance of the specified and returns it.
*
* @param className
- * fully qualified class name to be instanciated.
+ * fully qualified class name to be instantiated.
*
* @return null
* if it fails. Error messages will be printed by this method.
@@ -289,7 +285,7 @@ class SchemaFactoryFinder {
debugPrintln("createInstance(" + className + ")");
// get Class from className
- Class clazz = createClass(className);
+ Class> clazz = createClass(className);
if (clazz == null) {
debugPrintln("failed to getClass(" + className + ")");
return null;
@@ -298,9 +294,13 @@ class SchemaFactoryFinder {
// instantiate Class as a SchemaFactory
try {
- if (!useServicesMechanism) {
- schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz);
- }
+ if (!SchemaFactory.class.isAssignableFrom(clazz)) {
+ throw new ClassCastException(clazz.getName()
+ + " cannot be cast to " + SchemaFactory.class);
+ }
+ if (!useServicesMechanism) {
+ schemaFactory = newInstanceNoServiceLoader(clazz);
+ }
if (schemaFactory == null) {
schemaFactory = (SchemaFactory) clazz.newInstance();
}
@@ -326,11 +326,12 @@ class SchemaFactoryFinder {
return schemaFactory;
}
+
/**
- * Try to construct using newTransformerFactoryNoServiceLoader
+ * Try to construct using newXMLSchemaFactoryNoServiceLoader
* method if available.
*/
- private static Object newInstanceNoServiceLoader(
+ private static SchemaFactory newInstanceNoServiceLoader(
Class> providerClass
) {
// Retain maximum compatibility if no security manager.
@@ -338,196 +339,87 @@ class SchemaFactoryFinder {
return null;
}
try {
- Method creationMethod =
+ final Method creationMethod =
providerClass.getDeclaredMethod(
"newXMLSchemaFactoryNoServiceLoader"
);
- return creationMethod.invoke(null, (Object[])null);
- } catch (NoSuchMethodException exc) {
+ final int modifiers = creationMethod.getModifiers();
+
+ // Do not call the method if it's not public static.
+ if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
return null;
- } catch (Exception exc) {
- return null;
- }
- }
+ }
- /** Iterator that lazily computes one value and returns it. */
- private static abstract class SingleIterator implements Iterator {
- private boolean seen = false;
-
- public final void remove() { throw new UnsupportedOperationException(); }
- public final boolean hasNext() { return !seen; }
- public final Object next() {
- if(seen) throw new NoSuchElementException();
- seen = true;
- return value();
- }
-
- protected abstract Object value();
- }
-
- /**
- * Looks up a value in a property file
- * while producing all sorts of debug messages.
- *
- * @return null
- * if there was an error.
- */
- private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in )
- throws IOException {
- debugPrintln("Reading "+resourceName );
-
- Properties props=new Properties();
- props.load(in);
- in.close();
- String factoryClassName = props.getProperty(keyName);
- if(factoryClassName != null){
- debugPrintln("found "+keyName+" = " + factoryClassName);
- return createInstance(factoryClassName);
- } else {
- debugPrintln(keyName+" is not in the property file");
+ // Only calls "newXMLSchemaFactoryNoServiceLoader" if it's
+ // declared to return an instance of SchemaFactory.
+ final Class> returnType = creationMethod.getReturnType();
+ if (SERVICE_CLASS.isAssignableFrom(returnType)) {
+ return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null));
+ } else {
+ // Should not happen since
+ // XMLSchemaFactory.newXMLSchemaFactoryNoServiceLoader is
+ // declared to return XMLSchemaFactory.
+ throw new ClassCastException(returnType
+ + " cannot be cast to " + SERVICE_CLASS);
+ }
+ } catch(ClassCastException e) {
+ throw new SchemaFactoryConfigurationError(e.getMessage(), e);
+ } catch (NoSuchMethodException exc) {
+ return null;
+ } catch (Exception exc) {
return null;
}
}
- /**
- *
Look up a value in a property file.
- *
- *
Set debug to true to trace property evaluation.
- *
- * @param schemaLanguage Schema Language to support.
- * @param inputName Name of InputStream.
- * @param in InputStream of properties.
- *
- * @return SchemaFactory as determined by keyName value or null if there was an error.
- *
- * @throws IOException If IO error reading from in.
- */
- private SchemaFactory loadFromService(
- String schemaLanguage,
- String inputName,
- InputStream in)
- throws IOException {
-
- SchemaFactory schemaFactory = null;
- final Class[] stringClassArray = {"".getClass()};
- final Object[] schemaLanguageObjectArray = {schemaLanguage};
- final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported";
-
- debugPrintln("Reading " + inputName);
-
- // read from InputStream until a match is found
- BufferedReader configFile = new BufferedReader(new InputStreamReader(in));
- String line = null;
- while ((line = configFile.readLine()) != null) {
- // '#' is comment char
- int comment = line.indexOf("#");
- switch (comment) {
- case -1: break; // no comment
- case 0: line = ""; break; // entire line is a comment
- default: line = line.substring(0, comment); break; // trim comment
- }
-
- // trim whitespace
- line = line.trim();
-
- // any content left on line?
- if (line.length() == 0) {
- continue;
- }
-
- // line content is now the name of the class
- Class clazz = createClass(line);
- if (clazz == null) {
- continue;
- }
-
- // create an instance of the Class
- try {
- schemaFactory = (SchemaFactory) clazz.newInstance();
- } catch (ClassCastException classCastExcpetion) {
- schemaFactory = null;
- continue;
- } catch (InstantiationException instantiationException) {
- schemaFactory = null;
- continue;
- } catch (IllegalAccessException illegalAccessException) {
- schemaFactory = null;
- continue;
- }
-
- // does this Class support desired Schema?
- try {
- Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray);
- Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray);
- if (supported.booleanValue()) {
- break;
- }
- } catch (NoSuchMethodException noSuchMethodException) {
-
- } catch (IllegalAccessException illegalAccessException) {
-
- } catch (InvocationTargetException invocationTargetException) {
-
- }
- schemaFactory = null;
+ // Call isSchemaLanguageSupported with initial context.
+ private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory,
+ final String schemaLanguage,
+ AccessControlContext acc) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public Boolean run() {
+ return factory.isSchemaLanguageSupported(schemaLanguage);
}
-
- // clean up
- configFile.close();
-
- // return new instance of SchemaFactory or null
- return schemaFactory;
+ }, acc);
}
/**
- * Returns an {@link Iterator} that enumerates all
- * the META-INF/services files that we care.
+ * Finds a service provider subclass of SchemaFactory that supports the
+ * given schema language using the ServiceLoader.
+ *
+ * @param schemaLanguage The schema language for which we seek a factory.
+ * @return A SchemaFactory supporting the specified schema language, or null
+ * if none is found.
+ * @throws SchemaFactoryConfigurationError if a configuration error is found.
*/
- private Iterator createServiceFileIterator() {
- if (classLoader == null) {
- return new SingleIterator() {
- protected Object value() {
- ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader();
- //return (ClassLoader.getSystemResource( SERVICE_ID ));
- return ss.getResourceAsURL(classLoader, SERVICE_ID);
+ private SchemaFactory findServiceProvider(final String schemaLanguage) {
+ assert schemaLanguage != null;
+ // store current context.
+ final AccessControlContext acc = AccessController.getContext();
+ try {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public SchemaFactory run() {
+ final ServiceLoader loader =
+ ServiceLoader.load(SERVICE_CLASS);
+ for (SchemaFactory factory : loader) {
+ // restore initial context to call
+ // factory.isSchemaLanguageSupported
+ if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) {
+ return factory;
+ }
+ }
+ return null; // no factory found.
}
- };
- } else {
- try {
- //final Enumeration e = classLoader.getResources(SERVICE_ID);
- final Enumeration e = ss.getResources(classLoader, SERVICE_ID);
- if(!e.hasMoreElements()) {
- debugPrintln("no "+SERVICE_ID+" file was found");
- }
-
- // wrap it into an Iterator.
- return new Iterator() {
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- public boolean hasNext() {
- return e.hasMoreElements();
- }
-
- public Object next() {
- return e.nextElement();
- }
- };
- } catch (IOException e) {
- debugPrintln("failed to enumerate resources "+SERVICE_ID);
- if(debug) e.printStackTrace();
- return new ArrayList().iterator(); // empty iterator
- }
+ });
+ } catch (ServiceConfigurationError error) {
+ throw new SchemaFactoryConfigurationError(
+ "Provider for " + SERVICE_CLASS + " cannot be created", error);
}
}
- private static final Class SERVICE_CLASS = SchemaFactory.class;
- private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
+ private static final Class SERVICE_CLASS = SchemaFactory.class;
-
- private static String which( Class clazz ) {
+ private static String which( Class> clazz ) {
return which( clazz.getName(), clazz.getClassLoader() );
}
diff --git a/jaxp/src/javax/xml/xpath/XPathFactory.java b/jaxp/src/javax/xml/xpath/XPathFactory.java
index 1fd93a7a146..d4c60d60637 100644
--- a/jaxp/src/javax/xml/xpath/XPathFactory.java
+++ b/jaxp/src/javax/xml/xpath/XPathFactory.java
@@ -90,7 +90,7 @@ public abstract class XPathFactory {
* @throws RuntimeException When there is a failure in creating an
* XPathFactory for the default object model.
*/
- public static final XPathFactory newInstance() {
+ public static XPathFactory newInstance() {
try {
return newInstance(DEFAULT_OBJECT_MODEL_URI);
@@ -121,14 +121,17 @@ public abstract class XPathFactory {
* If present, the value is processed just like above.
*
*
- * The class loader is asked for service provider provider-configuration files matching javax.xml.xpath.XPathFactory
- * in the resource directory META-INF/services.
- * See the JAR File Specification for file format and parsing rules.
- * Each potential service provider is required to implement the method:
- *
- * The first service provider found in class loader order that supports the specified object model is returned.
+ * Use the service-provider loading facilities, defined by the
+ * {@link java.util.ServiceLoader} class, to attempt to locate and load an
+ * implementation of the service.
+ *
+ * Each potential service provider is required to implement the method
+ * {@link #isObjectModelSupported(String objectModel)}.
+ * The first service provider found that supports the specified object
+ * model is returned.
+ *
+ * In case of {@link java.util.ServiceConfigurationError} an
+ * {@link XPathFactoryConfigurationException} will be thrown.
*
*
* Platform default XPathFactory is located in a platform specific way.
@@ -152,43 +155,41 @@ public abstract class XPathFactory {
*
* @return Instance of an XPathFactory.
*
- * @throws XPathFactoryConfigurationException If the specified object model is unavailable.
+ * @throws XPathFactoryConfigurationException If the specified object model
+ * is unavailable, or if there is a configuration error.
* @throws NullPointerException If uri is null.
- * @throws IllegalArgumentException If uri is null
+ * @throws IllegalArgumentException If uri is null
* or uri.length() == 0.
*/
- public static final XPathFactory newInstance(final String uri)
+ public static XPathFactory newInstance(final String uri)
throws XPathFactoryConfigurationException {
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException(
+ "XPathFactory#newInstance(String uri) cannot be called with uri == null");
}
- if (uri.length() == 0) {
- throw new IllegalArgumentException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""
- );
- }
+ if (uri.length() == 0) {
+ throw new IllegalArgumentException(
+ "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"");
+ }
- ClassLoader classLoader = ss.getContextClassLoader();
+ ClassLoader classLoader = ss.getContextClassLoader();
if (classLoader == null) {
//use the current class loader
classLoader = XPathFactory.class.getClassLoader();
}
- XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri);
+ XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri);
- if (xpathFactory == null) {
- throw new XPathFactoryConfigurationException(
- "No XPathFactory implementation found for the object model: "
- + uri
- );
- }
+ if (xpathFactory == null) {
+ throw new XPathFactoryConfigurationException(
+ "No XPathFactory implementation found for the object model: "
+ + uri);
+ }
- return xpathFactory;
+ return xpathFactory;
}
/**
@@ -242,16 +243,14 @@ public abstract class XPathFactory {
ClassLoader cl = classLoader;
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException(
+ "XPathFactory#newInstance(String uri) cannot be called with uri == null");
}
- if (uri.length() == 0) {
- throw new IllegalArgumentException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""
- );
- }
+ if (uri.length() == 0) {
+ throw new IllegalArgumentException(
+ "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"");
+ }
if (cl == null) {
cl = ss.getContextClassLoader();
@@ -260,31 +259,32 @@ public abstract class XPathFactory {
XPathFactory f = new XPathFactoryFinder(cl).createInstance(factoryClassName);
if (f == null) {
- throw new XPathFactoryConfigurationException(
- "No XPathFactory implementation found for the object model: "
- + uri
- );
+ throw new XPathFactoryConfigurationException(
+ "No XPathFactory implementation found for the object model: "
+ + uri);
}
//if this factory supports the given schemalanguage return this factory else thrown exception
- if(f.isObjectModelSupported(uri)){
+ if (f.isObjectModelSupported(uri)) {
return f;
- }else{
- throw new XPathFactoryConfigurationException("Factory " + factoryClassName + " doesn't support given " + uri + " object model");
+ } else {
+ throw new XPathFactoryConfigurationException("Factory "
+ + factoryClassName + " doesn't support given " + uri
+ + " object model");
}
}
- /**
- *
Is specified object model supported by this XPathFactory?
- *
- * @param objectModel Specifies the object model which the returned XPathFactory will understand.
- *
- * @return true if XPathFactory supports objectModel, else false.
- *
- * @throws NullPointerException If objectModel is null.
- * @throws IllegalArgumentException If objectModel.length() == 0.
- */
- public abstract boolean isObjectModelSupported(String objectModel);
+ /**
+ *
Is specified object model supported by this XPathFactory?
+ *
+ * @param objectModel Specifies the object model which the returned XPathFactory will understand.
+ *
+ * @return true if XPathFactory supports objectModel, else false.
+ *
+ * @throws NullPointerException If objectModel is null.
+ * @throws IllegalArgumentException If objectModel.length() == 0.
+ */
+ public abstract boolean isObjectModelSupported(String objectModel);
/**
*
Set a feature for this XPathFactory and
@@ -314,8 +314,8 @@ public abstract class XPathFactory {
* it creates cannot support this feature.
* @throws NullPointerException if name is null.
*/
- public abstract void setFeature(String name, boolean value)
- throws XPathFactoryConfigurationException;
+ public abstract void setFeature(String name, boolean value)
+ throws XPathFactoryConfigurationException;
/**
*
Get the state of the named feature.
@@ -339,8 +339,8 @@ public abstract class XPathFactory {
* it creates cannot support this feature.
* @throws NullPointerException if name is null.
*/
- public abstract boolean getFeature(String name)
- throws XPathFactoryConfigurationException;
+ public abstract boolean getFeature(String name)
+ throws XPathFactoryConfigurationException;
/**
*
Establish a default variable resolver.
@@ -359,19 +359,19 @@ public abstract class XPathFactory {
public abstract void setXPathVariableResolver(XPathVariableResolver resolver);
/**
- *
Establish a default function resolver.
- *
- *
Any XPath objects constructed from this factory will
- * use the specified resolver by default.
- *
- *
A NullPointerException is thrown if
- * resolver is null.
- *
- * @param resolver XPath function resolver.
- *
- * @throws NullPointerException If resolver is
- * null.
- */
+ *
Establish a default function resolver.
+ *
+ *
Any XPath objects constructed from this factory will
+ * use the specified resolver by default.
+ *
+ *
A NullPointerException is thrown if
+ * resolver is null.
+ *
+ * @param resolver XPath function resolver.
+ *
+ * @throws NullPointerException If resolver is
+ * null.
+ */
public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver);
/**
diff --git a/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java b/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java
index 797b9556a0a..f99ddc8a0c2 100644
--- a/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java
+++ b/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2013, 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
@@ -25,20 +25,16 @@
package javax.xml.xpath;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
/**
* Implementation of {@link XPathFactory#newInstance(String)}.
@@ -50,7 +46,7 @@ import java.util.Properties;
class XPathFactoryFinder {
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal";
- private static SecuritySupport ss = new SecuritySupport() ;
+ private static final SecuritySupport ss = new SecuritySupport() ;
/** debug support code. */
private static boolean debug = false;
static {
@@ -65,12 +61,12 @@ class XPathFactoryFinder {
/**
*
Cache properties for performance.
*/
- private static Properties cacheProps = new Properties();
+ private static final Properties cacheProps = new Properties();
- /**
- *
@@ -93,9 +89,8 @@ class XPathFactoryFinder {
* to find XPathFactory.
*
* @param loader
- * to be used to load resource, {@link XPathFactory}, and
- * {@link SchemaFactoryLoader} implementations during
- * the resolution process.
+ * to be used to load resource and {@link XPathFactory}
+ * implementations during the resolution process.
* If this parameter is null, the default system class loader
* will be used.
*/
@@ -113,7 +108,7 @@ class XPathFactoryFinder {
return;
}
} catch( Throwable unused ) {
- ; // getContextClassLoader() undefined in JDK1.1
+ // getContextClassLoader() undefined in JDK1.1
}
if( classLoader==ClassLoader.getSystemClassLoader() ) {
@@ -126,7 +121,7 @@ class XPathFactoryFinder {
/**
*
Creates a new {@link XPathFactory} object for the specified
- * schema language.
+ * object model.
*
* @param uri
* Identifies the underlying object model.
@@ -136,8 +131,10 @@ class XPathFactoryFinder {
* @throws NullPointerException
* If the parameter is null.
*/
- public XPathFactory newFactory(String uri) {
- if(uri==null) throw new NullPointerException();
+ public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException {
+ if (uri == null) {
+ throw new NullPointerException();
+ }
XPathFactory f = _newFactory(uri);
if (f != null) {
debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri);
@@ -154,8 +151,8 @@ class XPathFactoryFinder {
*
* @return {@link XPathFactory} for the given object model.
*/
- private XPathFactory _newFactory(String uri) {
- XPathFactory xpathFactory;
+ private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException {
+ XPathFactory xpathFactory = null;
String propertyName = SERVICE_CLASS.getName() + ":" + uri;
@@ -166,7 +163,9 @@ class XPathFactoryFinder {
if(r!=null) {
debugPrintln("The value is '"+r+"'");
xpathFactory = createInstance(r, true);
- if(xpathFactory != null) return xpathFactory;
+ if (xpathFactory != null) {
+ return xpathFactory;
+ }
} else
debugPrintln("The property is undefined.");
} catch( Throwable t ) {
@@ -180,8 +179,6 @@ class XPathFactoryFinder {
String configFile = javah + File.separator +
"lib" + File.separator + "jaxp.properties";
- String factoryClassName = null ;
-
// try to read from $java.home/lib/jaxp.properties
try {
if(firstTime){
@@ -196,7 +193,7 @@ class XPathFactoryFinder {
}
}
}
- factoryClassName = cacheProps.getProperty(propertyName);
+ final String factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
@@ -211,23 +208,16 @@ class XPathFactoryFinder {
}
}
- // try META-INF/services files
- Iterator sitr = createServiceFileIterator();
- while(sitr.hasNext()) {
- URL resource = (URL)sitr.next();
- debugPrintln("looking into " + resource);
- try {
- xpathFactory = loadFromService(uri, resource.toExternalForm(),
- ss.getURLInputStream(resource));
- if (xpathFactory != null) {
- return xpathFactory;
- }
- } catch(IOException e) {
- if( debug ) {
- debugPrintln("failed to read "+resource);
- e.printStackTrace();
- }
- }
+ // Try with ServiceLoader
+ assert xpathFactory == null;
+ xpathFactory = findServiceProvider(uri);
+
+ // The following assertion should always be true.
+ // Uncomment it, recompile, and run with -ea in case of doubts:
+ // assert xpathFactory == null || xpathFactory.isObjectModelSupported(uri);
+
+ if (xpathFactory != null) {
+ return xpathFactory;
}
// platform default
@@ -245,8 +235,8 @@ class XPathFactoryFinder {
* @param className Name of class to create.
* @return Created class or null.
*/
- private Class createClass(String className) {
- Class clazz;
+ private Class> createClass(String className) {
+ Class clazz;
// make sure we have access to restricted packages
boolean internal = false;
if (System.getSecurityManager() != null) {
@@ -258,47 +248,54 @@ class XPathFactoryFinder {
// use approprite ClassLoader
try {
if (classLoader != null && !internal) {
- clazz = classLoader.loadClass(className);
+ clazz = Class.forName(className, false, classLoader);
} else {
clazz = Class.forName(className);
}
} catch (Throwable t) {
- if(debug) t.printStackTrace();
- return null;
+ if(debug) {
+ t.printStackTrace();
+ }
+ return null;
}
- return clazz;
+ return clazz;
}
/**
*
Creates an instance of the specified and returns it.
*
* @param className
- * fully qualified class name to be instanciated.
+ * fully qualified class name to be instantiated.
*
* @return null
* if it fails. Error messages will be printed by this method.
*/
- XPathFactory createInstance( String className ) {
+ XPathFactory createInstance( String className )
+ throws XPathFactoryConfigurationException
+ {
return createInstance( className, false );
}
- XPathFactory createInstance( String className, boolean useServicesMechanism ) {
+
+ XPathFactory createInstance( String className, boolean useServicesMechanism )
+ throws XPathFactoryConfigurationException
+ {
XPathFactory xPathFactory = null;
debugPrintln("createInstance(" + className + ")");
// get Class from className
- Class clazz = createClass(className);
+ Class> clazz = createClass(className);
if (clazz == null) {
- debugPrintln("failed to getClass(" + className + ")");
- return null;
+ debugPrintln("failed to getClass(" + className + ")");
+ return null;
}
debugPrintln("loaded " + className + " from " + which(clazz));
// instantiate Class as a XPathFactory
try {
if (!useServicesMechanism) {
- xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz);
+ xPathFactory = newInstanceNoServiceLoader(clazz);
}
if (xPathFactory == null) {
xPathFactory = (XPathFactory) clazz.newInstance();
@@ -329,203 +326,94 @@ class XPathFactoryFinder {
* Try to construct using newXPathFactoryNoServiceLoader
* method if available.
*/
- private static Object newInstanceNoServiceLoader(
+ private static XPathFactory newInstanceNoServiceLoader(
Class> providerClass
- ) {
+ ) throws XPathFactoryConfigurationException {
// Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) {
return null;
}
try {
Method creationMethod =
- providerClass.getDeclaredMethod(
- "newXPathFactoryNoServiceLoader"
- );
- return creationMethod.invoke(null, (Object[])null);
- } catch (NoSuchMethodException exc) {
+ providerClass.getDeclaredMethod(
+ "newXPathFactoryNoServiceLoader"
+ );
+ final int modifiers = creationMethod.getModifiers();
+
+ // Do not call "newXPathFactoryNoServiceLoader" if it's
+ // not public static.
+ if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
return null;
- } catch (Exception exc) {
- return null;
- }
- }
-
- /**
- *
Look up a value in a property file.
- *
- *
Set debug to true to trace property evaluation.
- *
- * @param objectModel URI of object model to support.
- * @param inputName Name of InputStream.
- * @param in InputStream of properties.
- *
- * @return XPathFactory as determined by keyName value or null if there was an error.
- *
- * @throws IOException If IO error reading from in.
- */
- private XPathFactory loadFromService(
- String objectModel,
- String inputName,
- InputStream in)
- throws IOException {
-
- XPathFactory xPathFactory = null;
- final Class[] stringClassArray = {"".getClass()};
- final Object[] objectModelObjectArray = {objectModel};
- final String isObjectModelSupportedMethod = "isObjectModelSupported";
-
- debugPrintln("Reading " + inputName);
-
- // read from InputStream until a match is found
- BufferedReader configFile = new BufferedReader(new InputStreamReader(in));
- String line = null;
- while ((line = configFile.readLine()) != null) {
- // '#' is comment char
- int comment = line.indexOf("#");
- switch (comment) {
- case -1: break; // no comment
- case 0: line = ""; break; // entire line is a comment
- default: line = line.substring(0, comment); break; // trim comment
- }
-
- // trim whitespace
- line = line.trim();
-
- // any content left on line?
- if (line.length() == 0) {
- continue;
- }
-
- // line content is now the name of the class
- Class clazz = createClass(line);
- if (clazz == null) {
- continue;
- }
-
- // create an instance of the Class
- try {
- xPathFactory = (XPathFactory) clazz.newInstance();
- } catch (ClassCastException classCastExcpetion) {
- xPathFactory = null;
- continue;
- } catch (InstantiationException instantiationException) {
- xPathFactory = null;
- continue;
- } catch (IllegalAccessException illegalAccessException) {
- xPathFactory = null;
- continue;
- }
-
- // does this Class support desired object model?
- try {
- Method isObjectModelSupported = clazz.getMethod(isObjectModelSupportedMethod, stringClassArray);
- Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray);
- if (supported.booleanValue()) {
- break;
- }
-
- } catch (NoSuchMethodException noSuchMethodException) {
-
- } catch (IllegalAccessException illegalAccessException) {
-
- } catch (InvocationTargetException invocationTargetException) {
-
- }
- xPathFactory = null;
}
- // clean up
- configFile.close();
-
- // return new instance of XPathFactory or null
- return xPathFactory;
- }
-
- /** Iterator that lazily computes one value and returns it. */
- private static abstract class SingleIterator implements Iterator {
- private boolean seen = false;
-
- public final void remove() { throw new UnsupportedOperationException(); }
- public final boolean hasNext() { return !seen; }
- public final Object next() {
- if(seen) throw new NoSuchElementException();
- seen = true;
- return value();
- }
-
- protected abstract Object value();
- }
-
- /**
- * Looks up a value in a property file
- * while producing all sorts of debug messages.
- *
- * @return null
- * if there was an error.
- */
- private XPathFactory loadFromProperty( String keyName, String resourceName, InputStream in )
- throws IOException {
- debugPrintln("Reading "+resourceName );
-
- Properties props = new Properties();
- props.load(in);
- in.close();
- String factoryClassName = props.getProperty(keyName);
- if(factoryClassName != null){
- debugPrintln("found "+keyName+" = " + factoryClassName);
- return createInstance(factoryClassName, true);
- } else {
- debugPrintln(keyName+" is not in the property file");
+ // Only calls "newXPathFactoryNoServiceLoader" if it's
+ // declared to return an instance of XPathFactory.
+ final Class> returnType = creationMethod.getReturnType();
+ if (SERVICE_CLASS.isAssignableFrom(returnType)) {
+ return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null));
+ } else {
+ // Should not happen since
+ // XPathFactoryImpl.newXPathFactoryNoServiceLoader is
+ // declared to return XPathFactory.
+ throw new ClassCastException(returnType
+ + " cannot be cast to " + SERVICE_CLASS);
+ }
+ } catch (ClassCastException e) {
+ throw new XPathFactoryConfigurationException(e);
+ } catch (NoSuchMethodException exc) {
+ return null;
+ } catch (Exception exc) {
return null;
}
}
+ // Call isObjectModelSupportedBy with initial context.
+ private boolean isObjectModelSupportedBy(final XPathFactory factory,
+ final String objectModel,
+ AccessControlContext acc) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public Boolean run() {
+ return factory.isObjectModelSupported(objectModel);
+ }
+ }, acc);
+ }
+
/**
- * Returns an {@link Iterator} that enumerates all
- * the META-INF/services files that we care.
+ * Finds a service provider subclass of XPathFactory that supports the
+ * given object model using the ServiceLoader.
+ *
+ * @param objectModel URI of object model to support.
+ * @return An XPathFactory supporting the specified object model, or null
+ * if none is found.
+ * @throws XPathFactoryConfigurationException if a configuration error is found.
*/
- private Iterator createServiceFileIterator() {
- if (classLoader == null) {
- return new SingleIterator() {
- protected Object value() {
- ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader();
- return ss.getResourceAsURL(classLoader, SERVICE_ID);
- //return (ClassLoader.getSystemResource( SERVICE_ID ));
+ private XPathFactory findServiceProvider(final String objectModel)
+ throws XPathFactoryConfigurationException {
+
+ assert objectModel != null;
+ // store current context.
+ final AccessControlContext acc = AccessController.getContext();
+ try {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public XPathFactory run() {
+ final ServiceLoader loader =
+ ServiceLoader.load(SERVICE_CLASS);
+ for (XPathFactory factory : loader) {
+ // restore initial context to call
+ // factory.isObjectModelSupportedBy
+ if (isObjectModelSupportedBy(factory, objectModel, acc)) {
+ return factory;
+ }
+ }
+ return null; // no factory found.
}
- };
- } else {
- try {
- //final Enumeration e = classLoader.getResources(SERVICE_ID);
- final Enumeration e = ss.getResources(classLoader, SERVICE_ID);
- if(!e.hasMoreElements()) {
- debugPrintln("no "+SERVICE_ID+" file was found");
- }
-
- // wrap it into an Iterator.
- return new Iterator() {
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- public boolean hasNext() {
- return e.hasMoreElements();
- }
-
- public Object next() {
- return e.nextElement();
- }
- };
- } catch (IOException e) {
- debugPrintln("failed to enumerate resources "+SERVICE_ID);
- if(debug) e.printStackTrace();
- return new ArrayList().iterator(); // empty iterator
- }
+ });
+ } catch (ServiceConfigurationError error) {
+ throw new XPathFactoryConfigurationException(error);
}
}
- private static final Class SERVICE_CLASS = XPathFactory.class;
- private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
-
-
+ private static final Class SERVICE_CLASS = XPathFactory.class;
private static String which( Class clazz ) {
return which( clazz.getName(), clazz.getClassLoader() );
From 77b43bacfdff5c3357240220c1dadeb5210f1e0c Mon Sep 17 00:00:00 2001
From: Jan Lahoda
Date: Wed, 17 Apr 2013 15:54:24 +0200
Subject: [PATCH 011/134] 8008174: DocTree API should provide start and end
positions for tree nodes
Adding DocSourcePositions to allow access to DocTree starting/ending position
Co-authored-by: Ralph Benjamin Ruijs
Reviewed-by: jjg, darcy
---
.../sun/source/util/DocSourcePositions.java | 98 ++++++++++++++++
.../classes/com/sun/source/util/DocTrees.java | 2 +
.../com/sun/source/util/SourcePositions.java | 2 +-
.../com/sun/tools/javac/api/JavacTrees.java | 88 ++++++++++++++-
.../tools/javac/parser/DocCommentParser.java | 11 +-
.../com/sun/tools/javac/tree/DCTree.java | 26 ++++-
.../javac/doctree/positions/TestPosition.java | 105 ++++++++++++++++++
.../javac/doctree/positions/TestPosition.out | 99 +++++++++++++++++
.../doctree/positions/TestPositionSource.java | 71 ++++++++++++
9 files changed, 488 insertions(+), 14 deletions(-)
create mode 100644 langtools/src/share/classes/com/sun/source/util/DocSourcePositions.java
create mode 100644 langtools/test/tools/javac/doctree/positions/TestPosition.java
create mode 100644 langtools/test/tools/javac/doctree/positions/TestPosition.out
create mode 100644 langtools/test/tools/javac/doctree/positions/TestPositionSource.java
diff --git a/langtools/src/share/classes/com/sun/source/util/DocSourcePositions.java b/langtools/src/share/classes/com/sun/source/util/DocSourcePositions.java
new file mode 100644
index 00000000000..d249da1c278
--- /dev/null
+++ b/langtools/src/share/classes/com/sun/source/util/DocSourcePositions.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+package com.sun.source.util;
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.CompilationUnitTree;
+
+/**
+ * Provides methods to obtain the position of a DocTree within a javadoc comment.
+ * A position is defined as a simple character offset from the start of a
+ * CompilationUnit where the first character is at offset 0.
+ *
+ * @since 1.8
+ */
+@jdk.Supported
+public interface DocSourcePositions extends SourcePositions {
+
+ /**
+ * Gets the starting position of the tree within the comment within the file. If tree is not found within
+ * file, or if the starting position is not available,
+ * return {@link javax.tools.Diagnostic#NOPOS}.
+ * The given tree should be under the given comment tree, and the given documentation
+ * comment tree should be returned from a {@link DocTrees#getDocCommentTree(com.sun.source.util.TreePath) }
+ * for a tree under the given file.
+ * The returned position must be at the start of the yield of this tree, that
+ * is for any sub-tree of this tree, the following must hold:
+ *
+ *
+ *
+ * @param file CompilationUnit in which to find tree.
+ * @param comment the comment tree that encloses the tree for which the
+ * position is being sought
+ * @param tree tree for which a position is sought.
+ * @return the start position of tree.
+ */
+ long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree);
+
+ /**
+ * Gets the ending position of the tree within the comment within the file. If tree is not found within
+ * file, or if the ending position is not available,
+ * return {@link javax.tools.Diagnostic#NOPOS}.
+ * The given tree should be under the given comment tree, and the given documentation
+ * comment tree should be returned from a {@link DocTrees#getDocCommentTree(com.sun.source.util.TreePath) }
+ * for a tree under the given file.
+ * The returned position must be at the end of the yield of this tree,
+ * that is for any sub-tree of this tree, the following must hold:
+ *
+ *
+ *
+ * @param file CompilationUnit in which to find tree.
+ * @param comment the comment tree that encloses the tree for which the
+ * position is being sought
+ * @param tree tree for which a position is sought.
+ * @return the start position of tree.
+ */
+ long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree);
+
+}
diff --git a/langtools/src/share/classes/com/sun/source/util/DocTrees.java b/langtools/src/share/classes/com/sun/source/util/DocTrees.java
index 63f6c899c20..94f68bbd4a7 100644
--- a/langtools/src/share/classes/com/sun/source/util/DocTrees.java
+++ b/langtools/src/share/classes/com/sun/source/util/DocTrees.java
@@ -72,6 +72,8 @@ public abstract class DocTrees extends Trees {
*/
public abstract Element getElement(TreePath path, ReferenceTree reference);
+ public abstract DocSourcePositions getSourcePositions();
+
/**
* Prints a message of the specified kind at the location of the
* tree within the provided compilation unit
diff --git a/langtools/src/share/classes/com/sun/source/util/SourcePositions.java b/langtools/src/share/classes/com/sun/source/util/SourcePositions.java
index d55c73135ab..4494d320434 100644
--- a/langtools/src/share/classes/com/sun/source/util/SourcePositions.java
+++ b/langtools/src/share/classes/com/sun/source/util/SourcePositions.java
@@ -59,7 +59,7 @@ public interface SourcePositions {
/**
* Gets the ending position of tree within file. If tree is not found within
- * file, or if the starting position is not available,
+ * file, or if the ending position is not available,
* return {@link javax.tools.Diagnostic#NOPOS}.
* The returned position must be at the end of the yield of this tree,
* that is for any sub-tree of this tree, the following must hold:
diff --git a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java
index 490490178fb..545b883f7f0 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -43,14 +43,16 @@ import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
+import com.sun.source.util.DocSourcePositions;
+import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
-import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Kinds;
@@ -76,8 +78,14 @@ import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.DCTree;
+import com.sun.tools.javac.tree.DCTree.DCBlockTag;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
+import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
+import com.sun.tools.javac.tree.DCTree.DCErroneous;
+import com.sun.tools.javac.tree.DCTree.DCIdentifier;
+import com.sun.tools.javac.tree.DCTree.DCParam;
import com.sun.tools.javac.tree.DCTree.DCReference;
+import com.sun.tools.javac.tree.DCTree.DCText;
import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.*;
@@ -94,6 +102,7 @@ import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Pair;
+import com.sun.tools.javac.util.Position;
import static com.sun.tools.javac.code.TypeTag.*;
/**
@@ -166,8 +175,8 @@ public class JavacTrees extends DocTrees {
javacTaskImpl = (JavacTaskImpl) t;
}
- public SourcePositions getSourcePositions() {
- return new SourcePositions() {
+ public DocSourcePositions getSourcePositions() {
+ return new DocSourcePositions() {
public long getStartPosition(CompilationUnitTree file, Tree tree) {
return TreeInfo.getStartPos((JCTree) tree);
}
@@ -176,9 +185,80 @@ public class JavacTrees extends DocTrees {
EndPosTable endPosTable = ((JCCompilationUnit) file).endPositions;
return TreeInfo.getEndPos((JCTree) tree, endPosTable);
}
+
+ public long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) {
+ return ((DCTree) tree).getSourcePosition((DCDocComment) comment);
+ }
+ @SuppressWarnings("fallthrough")
+ public long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) {
+ DCDocComment dcComment = (DCDocComment) comment;
+ if (tree instanceof DCEndPosTree) {
+ int endPos = ((DCEndPosTree) tree).getEndPos(dcComment);
+
+ if (endPos != Position.NOPOS) {
+ return endPos;
+ }
+ }
+ int correction = 0;
+ switch (tree.getKind()) {
+ case TEXT:
+ DCText text = (DCText) tree;
+
+ return dcComment.comment.getSourcePos(text.pos + text.text.length());
+ case ERRONEOUS:
+ DCErroneous err = (DCErroneous) tree;
+
+ return dcComment.comment.getSourcePos(err.pos + err.body.length());
+ case IDENTIFIER:
+ DCIdentifier ident = (DCIdentifier) tree;
+
+ return dcComment.comment.getSourcePos(ident.pos + (ident.name != names.error ? ident.name.length() : 0));
+ case PARAM:
+ DCParam param = (DCParam) tree;
+
+ if (param.isTypeParameter && param.getDescription().isEmpty()) {
+ correction = 1;
+ }
+ case AUTHOR: case DEPRECATED: case RETURN: case SEE:
+ case SERIAL: case SERIAL_DATA: case SERIAL_FIELD: case SINCE:
+ case THROWS: case UNKNOWN_BLOCK_TAG: case VERSION: {
+ DocTree last = getLastChild(tree);
+
+ if (last != null) {
+ return getEndPosition(file, comment, last) + correction;
+ }
+
+ DCBlockTag block = (DCBlockTag) tree;
+
+ return dcComment.comment.getSourcePos(block.pos + block.getTagName().length() + 1);
+ }
+ default:
+ DocTree last = getLastChild(tree);
+
+ if (last != null) {
+ return getEndPosition(file, comment, last);
+ }
+ break;
+ }
+
+ return Position.NOPOS;
+ }
};
}
+ private DocTree getLastChild(DocTree tree) {
+ final DocTree[] last = new DocTree[] {null};
+
+ tree.accept(new DocTreeScanner() {
+ @Override public Void scan(DocTree node, Void p) {
+ if (node != null) last[0] = node;
+ return null;
+ }
+ }, null);
+
+ return last[0];
+ }
+
public JCClassDecl getTree(TypeElement element) {
return (JCClassDecl) getTree((Element) element);
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java b/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
index bbc4d81d5c7..535dbd2692e 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
@@ -41,6 +41,7 @@ import com.sun.tools.javac.tree.DCTree;
import com.sun.tools.javac.tree.DCTree.DCAttribute;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
import com.sun.tools.javac.tree.DCTree.DCEndElement;
+import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
import com.sun.tools.javac.tree.DCTree.DCErroneous;
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
import com.sun.tools.javac.tree.DCTree.DCReference;
@@ -336,12 +337,12 @@ public class DocCommentParser {
DCTree text = inlineText();
if (text != null) {
nextChar();
- return m.at(p).UnknownInlineTag(name, List.of(text));
+ return m.at(p).UnknownInlineTag(name, List.of(text)).setEndPos(bp);
}
} else if (tp.getKind() == TagParser.Kind.INLINE) {
- DCTree tree = tp.parse(p);
+ DCEndPosTree> tree = (DCEndPosTree>) tp.parse(p);
if (tree != null) {
- return tree;
+ return tree.setEndPos(bp);
}
} else {
inlineText(); // skip content
@@ -509,7 +510,7 @@ public class DocCommentParser {
fac.log.popDiagnosticHandler(deferredDiagnosticHandler);
}
- return m.at(pos).Reference(sig, qualExpr, member, paramTypes);
+ return m.at(pos).Reference(sig, qualExpr, member, paramTypes).setEndPos(bp);
}
JCTree parseType(String s) throws ParseException {
@@ -741,7 +742,7 @@ public class DocCommentParser {
}
if (ch == '>') {
nextChar();
- return m.at(p).StartElement(name, attrs, selfClosing);
+ return m.at(p).StartElement(name, attrs, selfClosing).setEndPos(bp);
}
}
} else if (ch == '/') {
diff --git a/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java b/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java
index c2ca23caf4b..2f4e6dd3a48 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java
@@ -36,6 +36,7 @@ import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
import java.io.IOException;
import java.io.StringWriter;
import javax.tools.JavaFileObject;
@@ -82,8 +83,24 @@ public abstract class DCTree implements DocTree {
return s.toString();
}
+ public static abstract class DCEndPosTree> extends DCTree {
+
+ private int endPos = Position.NOPOS;
+
+ public int getEndPos(DCDocComment dc) {
+ return dc.comment.getSourcePos(endPos);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T setEndPos(int endPos) {
+ this.endPos = endPos;
+ return (T) this;
+ }
+
+ }
+
public static class DCDocComment extends DCTree implements DocCommentTree {
- final Comment comment; // required for the implicit source pos table
+ public final Comment comment; // required for the implicit source pos table
public final List firstSentence;
public final List body;
@@ -125,7 +142,7 @@ public abstract class DCTree implements DocTree {
}
}
- public static abstract class DCInlineTag extends DCTree implements InlineTagTree {
+ public static abstract class DCInlineTag extends DCEndPosTree implements InlineTagTree {
public String getTagName() {
return getKind().tagName;
}
@@ -345,6 +362,7 @@ public abstract class DCTree implements DocTree {
public int getEndPosition(EndPosTable endPosTable) {
return pos + body.length();
}
+
}
public static class DCIdentifier extends DCTree implements IdentifierTree {
@@ -478,7 +496,7 @@ public abstract class DCTree implements DocTree {
}
}
- public static class DCReference extends DCTree implements ReferenceTree {
+ public static class DCReference extends DCEndPosTree implements ReferenceTree {
public final String signature;
// The following are not directly exposed through ReferenceTree
@@ -663,7 +681,7 @@ public abstract class DCTree implements DocTree {
}
}
- public static class DCStartElement extends DCTree implements StartElementTree {
+ public static class DCStartElement extends DCEndPosTree implements StartElementTree {
public final Name name;
public final List attrs;
public final boolean selfClosing;
diff --git a/langtools/test/tools/javac/doctree/positions/TestPosition.java b/langtools/test/tools/javac/doctree/positions/TestPosition.java
new file mode 100644
index 00000000000..8d03c8d165f
--- /dev/null
+++ b/langtools/test/tools/javac/doctree/positions/TestPosition.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8008174
+ * @summary proper source positions for doc comments
+ * @build TestPosition
+ * @compile/ref=TestPosition.out -processor TestPosition -proc:only TestPositionSource.java
+ */
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.DocSourcePositions;
+import com.sun.source.util.DocTreeScanner;
+import com.sun.source.util.DocTrees;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import java.io.IOException;
+import java.util.Set;
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+
+@SupportedAnnotationTypes("*")
+public class TestPosition extends AbstractProcessor {
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ TypeElement source = processingEnv.getElementUtils().getTypeElement("TestPositionSource");
+
+ if (source == null) throw new IllegalStateException();
+
+ if (!roundEnv.getRootElements().contains(source)) return false;
+
+ final DocTrees trees = DocTrees.instance(processingEnv);
+ final TreePath testElement = trees.getPath(source);
+
+ if (testElement == null) throw new IllegalStateException();
+
+ String code;
+
+ try {
+ code = testElement.getCompilationUnit().getSourceFile().getCharContent(false).toString();
+ } catch ( IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ new TreePathScanner() {
+ @Override public Void visitMethod(MethodTree node, Void p) {
+ final DocCommentTree docCommentTree = trees.getDocCommentTree(getCurrentPath());
+
+ if (docCommentTree != null) {
+ System.out.println(node.getName() + ":");
+ new DocTreeScanner() {
+ @Override public Void scan(DocTree node, Void p) {
+ if (node != null) {
+ DocSourcePositions sp = (DocSourcePositions) trees.getSourcePositions(); //XXX: the cast???
+ int start = (int) sp.getStartPosition(testElement.getCompilationUnit(), docCommentTree, node);
+ int end = (int) sp.getEndPosition(testElement.getCompilationUnit(), docCommentTree, node);
+ String snippet = code.substring(start, end).replace(" \n", "!trailing-whitespace!\n");
+
+ if (snippet.endsWith(" ")) {
+ snippet = snippet.substring(0, snippet.length() - 1) + "!trailing-whitespace!";
+ }
+ System.out.println(node.getKind().name() + ":" + snippet);
+ }
+ return super.scan(node, p);
+ }
+ }.scan(docCommentTree, null);
+ }
+
+ return super.visitMethod(node, p);
+ }
+ }.scan(testElement, null);
+
+ return false;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+}
diff --git a/langtools/test/tools/javac/doctree/positions/TestPosition.out b/langtools/test/tools/javac/doctree/positions/TestPosition.out
new file mode 100644
index 00000000000..36a386866a8
--- /dev/null
+++ b/langtools/test/tools/javac/doctree/positions/TestPosition.out
@@ -0,0 +1,99 @@
+valid:
+DOC_COMMENT:First sentence.
+ *
+ *
Description with {@link }, {@link java.util.List#add( int )},
+ * {@link java.util.List#add( int ) some text with whitespaces}, {@link
+ *
+ * @param first
+ * @param second some text with trailing whitespace
+ * @return some return
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalStateException some text
+TEXT:First sentence.
+START_ELEMENT:
+TEXT:Description with!trailing-whitespace!
+LINK:{@link }
+TEXT:,!trailing-whitespace!
+LINK:{@link java.util.List#add( int )}
+REFERENCE:java.util.List#add( int )
+TEXT:,
+ *!trailing-whitespace!
+LINK:{@link java.util.List#add( int ) some text with whitespaces}
+REFERENCE:java.util.List#add( int )
+TEXT:some text with whitespaces
+TEXT:,!trailing-whitespace!
+ERRONEOUS:{@link
+PARAM:@param first
+IDENTIFIER:first
+PARAM:@param second some text with trailing whitespace
+IDENTIFIER:second
+TEXT:some text with trailing whitespace
+RETURN:@return some return
+TEXT:some return
+THROWS:@throws java.lang.IllegalStateException
+REFERENCE:java.lang.IllegalStateException
+THROWS:@throws java.lang.IllegalStateException some text
+REFERENCE:java.lang.IllegalStateException
+TEXT:some text
\ No newline at end of file
diff --git a/langtools/test/tools/javac/doctree/positions/TestPositionSource.java b/langtools/test/tools/javac/doctree/positions/TestPositionSource.java
new file mode 100644
index 00000000000..801af3d687c
--- /dev/null
+++ b/langtools/test/tools/javac/doctree/positions/TestPositionSource.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ *
+ * 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.
+ */
+public class TestPositionSource {
+
+ /**First sentence.
+ *
+ *
Description with {@link }, {@link java.util.List#add( int )},
+ * {@link java.util.List#add( int ) some text with whitespaces}, {@link
+ *
+ * @param first
+ * @param second some text with trailing whitespace
+ * @return some return
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalStateException some text
+ */
+ public boolean withWhiteSpaces(int first, int second) throws IllegalStateException {
+ return true;
+ }
+
+}
From b8f4f275b47f6a27b6aad9d02e76378b7b795c28 Mon Sep 17 00:00:00 2001
From: James Laskey
Date: Wed, 17 Apr 2013 15:36:48 -0300
Subject: [PATCH 012/134] 8012529: Remove -esa from testing jvmargs
Reviewed-by: sundar
---
nashorn/make/project.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nashorn/make/project.properties b/nashorn/make/project.properties
index bd0d7ec6625..e2f39bb7ab0 100644
--- a/nashorn/make/project.properties
+++ b/nashorn/make/project.properties
@@ -214,7 +214,7 @@ run.test.xms=2G
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
-run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
+run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
From 649b7e005b96b7382d34022da0b56e500f5651e9 Mon Sep 17 00:00:00 2001
From: Athijegannathan Sundararajan
Date: Thu, 18 Apr 2013 15:50:30 +0530
Subject: [PATCH 013/134] 8012462: Date.prototype.toJSON does not handle
non-Date 'this' as per the spec
Reviewed-by: jlaskey, hannesw
---
.../nashorn/internal/objects/NativeDate.java | 10 ++--
nashorn/test/script/basic/JDK-8012462.js | 47 +++++++++++++++++++
2 files changed, 52 insertions(+), 5 deletions(-)
create mode 100644 nashorn/test/script/basic/JDK-8012462.js
diff --git a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java
index 4724cda136b..1be4cf78a94 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java
@@ -851,11 +851,11 @@ public final class NativeDate extends ScriptObject {
}
final ScriptObject sobj = (ScriptObject)selfObj;
final Object value = sobj.getDefaultValue(Number.class);
-
- final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN;
-
- if (isInfinite(num) || isNaN(num)) {
- return null;
+ if (value instanceof Number) {
+ final double num = ((Number)value).doubleValue();
+ if (isInfinite(num) || isNaN(num)) {
+ return null;
+ }
}
try {
diff --git a/nashorn/test/script/basic/JDK-8012462.js b/nashorn/test/script/basic/JDK-8012462.js
new file mode 100644
index 00000000000..4e015a99df7
--- /dev/null
+++ b/nashorn/test/script/basic/JDK-8012462.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8012462: Date.prototype.toJSON does not handle non-Date 'this' as per the spec.
+ *
+ * @test
+ * @run
+ */
+
+var toJSON = Date.prototype.toJSON;
+
+function checkJSON(value, expected) {
+ var res = toJSON.call({
+ valueOf: function() { return value; },
+ toISOString: function() { return value; }
+ });
+
+ if (res !== expected) {
+ fail("Date.prototype.toJSON does not work for non-Date 'this'");
+ }
+}
+
+checkJSON(NaN, null);
+checkJSON(-Infinity, null);
+checkJSON(Infinity, null);
+checkJSON("foo", "foo");
From 4239700710404bd90a86da38dc591e522fe343d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?=
Date: Thu, 18 Apr 2013 14:25:45 +0200
Subject: [PATCH 014/134] 8012460: RegExp regression
Reviewed-by: jlaskey, sundar
---
.../internal/runtime/regexp/joni/Parser.java | 5 +-
nashorn/test/script/basic/JDK-8012460.js | 59 +++++++++++++++++++
.../test/script/basic/JDK-8012460.js.EXPECTED | 18 ++++++
3 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 nashorn/test/script/basic/JDK-8012460.js
create mode 100644 nashorn/test/script/basic/JDK-8012460.js.EXPECTED
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java b/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
index 1b5a5861002..13d569b7b08 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
@@ -287,7 +287,10 @@ class Parser extends Lexer {
if (syntax.allowDoubleRangeOpInCC()) {
env.ccEscWarn("-");
- parseCharClassSbChar(cc, arg); // goto sb_char /* [0-9-a] is allowed as [0-9\-a] */
+ arg.inType = CCVALTYPE.SB;
+ arg.v = '-';
+ arg.vIsRaw = false;
+ parseCharClassValEntry2(cc, arg); // goto val_entry2 /* [0-9-a] is allowed as [0-9\-a] */
break;
}
newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS);
diff --git a/nashorn/test/script/basic/JDK-8012460.js b/nashorn/test/script/basic/JDK-8012460.js
new file mode 100644
index 00000000000..0b41298d640
--- /dev/null
+++ b/nashorn/test/script/basic/JDK-8012460.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2013, 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8012460: RegExp regression
+ *
+ * @test
+ * @run
+ */
+
+
+var semver = "\\s*[v=]*\\s*([0-9]+)" // major
+ + "\\.([0-9]+)" // minor
+ + "\\.([0-9]+)" // patch
+ + "(-[0-9]+-?)?" // build
+ + "([a-zA-Z-+][a-zA-Z0-9-\.:]*)?" // tag
+ , exprComparator = "^((<|>)?=?)\s*("+semver+")$|^$";
+var validComparator = new RegExp("^"+exprComparator+"$");
+
+
+print(exprComparator);
+print(">=0.6.0-".match(validComparator));
+print("=0.6.0-".match(validComparator));
+print("0.6.0-".match(validComparator));
+print("<=0.6.0-".match(validComparator));
+print(">=0.6.0-a:b-c.d".match(validComparator));
+print("=0.6.0-a:b-c.d".match(validComparator));
+print("0.6.0+a:b-c.d".match(validComparator));
+print("<=0.6.0+a:b-c.d".match(validComparator));
+
+print(/[a-zA-Z-+]/.exec("a"));
+print(/[a-zA-Z-+]/.exec("b"));
+print(/[a-zA-Z-+]/.exec("y"));
+print(/[a-zA-Z-+]/.exec("z"));
+print(/[a-zA-Z-+]/.exec("B"));
+print(/[a-zA-Z-+]/.exec("Y"));
+print(/[a-zA-Z-+]/.exec("Z"));
+print(/[a-zA-Z-+]/.exec("-"));
+print(/[a-zA-Z-+]/.exec("+"));
diff --git a/nashorn/test/script/basic/JDK-8012460.js.EXPECTED b/nashorn/test/script/basic/JDK-8012460.js.EXPECTED
new file mode 100644
index 00000000000..917cac5422f
--- /dev/null
+++ b/nashorn/test/script/basic/JDK-8012460.js.EXPECTED
@@ -0,0 +1,18 @@
+^((<|>)?=?)s*(\s*[v=]*\s*([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]+-?)?([a-zA-Z-+][a-zA-Z0-9-.:]*)?)$|^$
+>=0.6.0-,>=,>,0.6.0-,0,6,0,,-
+=0.6.0-,=,,0.6.0-,0,6,0,,-
+0.6.0-,,,0.6.0-,0,6,0,,-
+<=0.6.0-,<=,<,0.6.0-,0,6,0,,-
+>=0.6.0-a:b-c.d,>=,>,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+=0.6.0-a:b-c.d,=,,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+0.6.0+a:b-c.d,,,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+<=0.6.0+a:b-c.d,<=,<,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+a
+b
+y
+z
+B
+Y
+Z
+-
++
From 66dde86ad051a038344c05dfaabc874036ae659a Mon Sep 17 00:00:00 2001
From: Jonathan Gibbons
Date: Thu, 18 Apr 2013 19:58:45 -0700
Subject: [PATCH 015/134] 8012658: Change default langtools source level to 7
Reviewed-by: darcy
---
.../netbeans/langtools/nbproject/project.xml | 26 ++++++++-----------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/langtools/make/netbeans/langtools/nbproject/project.xml b/langtools/make/netbeans/langtools/nbproject/project.xml
index 4aba328a0ac..710d523bfae 100644
--- a/langtools/make/netbeans/langtools/nbproject/project.xml
+++ b/langtools/make/netbeans/langtools/nbproject/project.xml
@@ -1,6 +1,6 @@
-
build
@@ -144,7 +144,7 @@
-
@@ -178,7 +178,7 @@
-
@@ -239,10 +239,6 @@
${root}/make
-
-
- ${root}/src/share/classes
- README
@@ -250,7 +246,7 @@
-
@@ -305,11 +301,11 @@
-
+ ${root}/src/share/classes${root}/build/classes
- 1.5
+ 1.7
From 3c7d12fc052eea80f1cff0492bf2a4d76ee4a856 Mon Sep 17 00:00:00 2001
From: Jonathan Gibbons
Date: Thu, 18 Apr 2013 20:00:14 -0700
Subject: [PATCH 016/134] 8012656: cache frequently used name strings for
DocImpl classes
Reviewed-by: darcy
---
.../com/sun/tools/javadoc/ClassDocImpl.java | 21 ++++++++++++++++---
.../com/sun/tools/javadoc/FieldDocImpl.java | 16 +++++++++++---
.../com/sun/tools/javadoc/MethodDocImpl.java | 16 +++++++++++---
.../com/sun/tools/javadoc/PackageDocImpl.java | 15 ++++++++-----
4 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java
index da28e39a630..b6f7d66dc38 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java
@@ -341,9 +341,14 @@ public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc {
*
*/
public String name() {
- return getClassName(tsym, false);
+ if (name == null) {
+ name = getClassName(tsym, false);
+ }
+ return name;
}
+ private String name;
+
/**
* Return the qualified class name as a String.
*
@@ -354,9 +359,14 @@ public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc {
*
*/
public String qualifiedName() {
- return getClassName(tsym, true);
+ if (qualifiedName == null) {
+ qualifiedName = getClassName(tsym, true);
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Return unqualified name of type excluding any dimension information.
*
@@ -380,9 +390,14 @@ public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc {
* Return the simple name of this type.
*/
public String simpleTypeName() {
- return tsym.name.toString();
+ if (simpleTypeName == null) {
+ simpleTypeName = tsym.name.toString();
+ }
+ return simpleTypeName;
}
+ private String simpleTypeName;
+
/**
* Return the qualified name and any type parameters.
* Each parameter is a type variable with optional bounds.
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
index e8e8f59d55c..48e25d6c73d 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -252,13 +252,23 @@ public class FieldDocImpl extends MemberDocImpl implements FieldDoc {
}
public String name() {
- return sym.name.toString();
+ if (name == null) {
+ name = sym.name.toString();
+ }
+ return name;
}
+ private String name;
+
public String qualifiedName() {
- return sym.enclClass().getQualifiedName() + "." + name();
+ if (qualifiedName == null) }
+ qualifiedName = sym.enclClass().getQualifiedName() + "." + name();
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Return the source position of the entity, or null if
* no position is available.
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java
index d8c03a8c980..4d5d191b929 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -203,13 +203,23 @@ public class MethodDocImpl
public String name() {
- return sym.name.toString();
+ if (name == null) {
+ name = sym.name.toString();
+ }
+ return name;
}
+ private String name;
+
public String qualifiedName() {
- return sym.enclClass().getQualifiedName() + "." + sym.name;
+ if (qualifiedName == null) {
+ qualifiedName = sym.enclClass().getQualifiedName() + "." + sym.name;
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Returns a string representation of this method. Includes the
* qualified signature, the qualified method name, and any type
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java
index bc54bef6912..2f11b81134e 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -334,12 +334,17 @@ public class PackageDocImpl extends DocImpl implements PackageDoc {
* Get package name.
*/
public String qualifiedName() {
- Name fullname = sym.getQualifiedName();
- // Some bogus tests depend on the interned "" being returned.
- // See 6457276.
- return fullname.isEmpty() ? "" : fullname.toString();
+ if (qualifiedName == null) {
+ Name fullname = sym.getQualifiedName();
+ // Some bogus tests depend on the interned "" being returned.
+ // See 6457276.
+ qualifiedName = fullname.isEmpty() ? "" : fullname.toString();
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* set doc path for an unzipped directory
*/
From 51d2ddd79024fc32b5ba4fe7f2c8919e0ac39f37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joel=20Borggr=C3=A9n-Franck?=
Date: Fri, 19 Apr 2013 11:57:46 +0200
Subject: [PATCH 017/134] 8012681: Commit for JDK-8012656 breaks tl build
Reviewed-by: vromero, chegar, alanb
---
.../src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
index 48e25d6c73d..1cec66ecd84 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java
@@ -261,7 +261,7 @@ public class FieldDocImpl extends MemberDocImpl implements FieldDoc {
private String name;
public String qualifiedName() {
- if (qualifiedName == null) }
+ if (qualifiedName == null) {
qualifiedName = sym.enclClass().getQualifiedName() + "." + name();
}
return qualifiedName;
From 865a11d502e862d630bce4bc4c7b24768af6dabb Mon Sep 17 00:00:00 2001
From: Athijegannathan Sundararajan
Date: Fri, 19 Apr 2013 17:46:01 +0530
Subject: [PATCH 018/134] 8012612: Compile failed
Reviewed-by: hannesw, jlaskey, attila
---
.../jdk/nashorn/internal/runtime/Context.java | 21 ++-----------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/Context.java b/nashorn/src/jdk/nashorn/internal/runtime/Context.java
index 6a7276154dd..b667a651b94 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java
@@ -56,8 +56,6 @@ import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.options.Options;
-import sun.reflect.CallerSensitive;
-import sun.reflect.Reflection;
/**
* This class manages the global state of execution. Context is immutable.
@@ -114,24 +112,9 @@ public final class Context {
* Get the current global scope
* @return the current global scope
*/
- @CallerSensitive
public static ScriptObject getGlobal() {
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // skip getCallerClass and getGlobal and get to the real caller
- Class> caller = Reflection.getCallerClass();
- ClassLoader callerLoader = caller.getClassLoader();
-
- // Allow this method only for nashorn's own classes, objects
- // package classes and Java adapter classes. Rest should
- // have the necessary security permission.
- if (callerLoader != myLoader &&
- !(callerLoader instanceof StructureLoader) &&
- !(JavaAdapterFactory.isAdapterClass(caller))) {
- sm.checkPermission(new RuntimePermission("nashorn.getGlobal"));
- }
- }
-
+ // This class in a package.access protected package.
+ // Trusted code only can call this method.
return getGlobalTrusted();
}
From f9a53d819fabd31fd0a8a1875efc0ad8c6d86514 Mon Sep 17 00:00:00 2001
From: Athijegannathan Sundararajan
Date: Fri, 19 Apr 2013 18:23:00 +0530
Subject: [PATCH 019/134] 8012593: JSAdapter overrides impacts strongly
construction time
Reviewed-by: jlaskey, attila
---
.../src/jdk/nashorn/internal/objects/NativeJSAdapter.java | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
index 409b9fa4c14..8a3f42d2c1c 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
@@ -148,11 +148,7 @@ public final class NativeJSAdapter extends ScriptObject {
if (overrides instanceof ScriptObject) {
this.overrides = true;
final ScriptObject sobj = (ScriptObject)overrides;
- final Iterator iter = sobj.propertyIterator();
- while (iter.hasNext()) {
- final String prop = iter.next();
- super.set(prop, sobj.get(prop), false);
- }
+ this.addBoundProperties(sobj);
} else {
this.overrides = false;
}
From fb7a325141bc1ac8676330f5d4306ec4a0b284cb Mon Sep 17 00:00:00 2001
From: Marcus Lagergren
Date: Fri, 19 Apr 2013 16:11:16 +0200
Subject: [PATCH 020/134] 8010701: Immutable nodes - final iteration
Reviewed-by: sundar, hannesw, jlaskey
---
nashorn/bin/verbose_octane.sh | 2 +-
.../api/scripting/NashornScriptEngine.java | 5 +-
.../jdk/nashorn/internal/codegen/Attr.java | 961 ++++++-------
.../internal/codegen/ClassEmitter.java | 39 +-
.../internal/codegen/CodeGenerator.java | 1218 ++++++++---------
.../internal/codegen/CompilationPhase.java | 260 ++--
.../nashorn/internal/codegen/Compiler.java | 62 +-
.../internal/codegen/CompilerConstants.java | 35 +-
.../internal/codegen/FieldObjectCreator.java | 5 +-
.../internal/codegen/FinalizeTypes.java | 215 ++-
.../internal/codegen/FoldConstants.java | 14 +-
.../jdk/nashorn/internal/codegen/Frame.java | 196 ---
.../jdk/nashorn/internal/codegen/Lower.java | 969 +++++--------
.../internal/codegen/MethodEmitter.java | 177 +--
.../nashorn/internal/codegen/Namespace.java | 8 +-
.../codegen/ObjectClassGenerator.java | 8 +-
.../internal/codegen/ObjectCreator.java | 3 +-
.../internal/codegen/SplitMethodEmitter.java | 100 ++
.../nashorn/internal/codegen/Splitter.java | 266 ++--
.../nashorn/internal/codegen/WeighNodes.java | 39 +-
.../jdk/nashorn/internal/ir/AccessNode.java | 103 +-
.../src/jdk/nashorn/internal/ir/BaseNode.java | 90 +-
.../jdk/nashorn/internal/ir/BinaryNode.java | 82 +-
.../src/jdk/nashorn/internal/ir/Block.java | 265 ++--
.../internal/ir/BlockLexicalContext.java | 120 ++
.../jdk/nashorn/internal/ir/BreakNode.java | 53 +-
.../nashorn/internal/ir/BreakableNode.java | 42 +-
.../src/jdk/nashorn/internal/ir/CallNode.java | 165 +--
.../src/jdk/nashorn/internal/ir/CaseNode.java | 47 +-
.../jdk/nashorn/internal/ir/CatchNode.java | 87 +-
.../jdk/nashorn/internal/ir/ContinueNode.java | 47 +-
.../jdk/nashorn/internal/ir/EmptyNode.java | 7 +-
.../jdk/nashorn/internal/ir/ExecuteNode.java | 46 +-
.../src/jdk/nashorn/internal/ir/Flags.java | 69 +
.../src/jdk/nashorn/internal/ir/ForNode.java | 165 ++-
.../jdk/nashorn/internal/ir/FunctionNode.java | 951 ++++---------
.../jdk/nashorn/internal/ir/IdentNode.java | 85 +-
.../src/jdk/nashorn/internal/ir/IfNode.java | 62 +-
.../jdk/nashorn/internal/ir/IndexNode.java | 93 +-
.../jdk/nashorn/internal/ir/LabelNode.java | 92 +-
.../jdk/nashorn/internal/ir/LabeledNode.java | 123 --
.../nashorn/internal/ir/LexicalContext.java | 624 +++++++--
...WhileNode.java => LexicalContextNode.java} | 57 +-
.../nashorn/internal/ir/LineNumberNode.java | 16 +-
.../jdk/nashorn/internal/ir/LiteralNode.java | 72 +-
.../src/jdk/nashorn/internal/ir/Location.java | 20 +-
.../src/jdk/nashorn/internal/ir/LoopNode.java | 176 +++
nashorn/src/jdk/nashorn/internal/ir/Node.java | 229 +---
.../jdk/nashorn/internal/ir/ObjectNode.java | 38 +-
.../jdk/nashorn/internal/ir/PropertyNode.java | 97 +-
.../jdk/nashorn/internal/ir/ReturnNode.java | 67 +-
.../jdk/nashorn/internal/ir/RuntimeNode.java | 75 +-
.../jdk/nashorn/internal/ir/SplitNode.java | 177 +--
.../jdk/nashorn/internal/ir/SwitchNode.java | 148 +-
.../src/jdk/nashorn/internal/ir/Symbol.java | 94 +-
.../jdk/nashorn/internal/ir/TernaryNode.java | 95 +-
.../jdk/nashorn/internal/ir/ThrowNode.java | 48 +-
.../src/jdk/nashorn/internal/ir/TryNode.java | 167 ++-
.../jdk/nashorn/internal/ir/UnaryNode.java | 61 +-
.../src/jdk/nashorn/internal/ir/VarNode.java | 131 +-
.../jdk/nashorn/internal/ir/WhileNode.java | 184 ++-
.../src/jdk/nashorn/internal/ir/WithNode.java | 65 +-
.../internal/ir/annotations/Immutable.java | 34 +
.../nashorn/internal/ir/debug/ASTWriter.java | 10 +-
.../nashorn/internal/ir/debug/JSONWriter.java | 238 ++--
.../internal/ir/debug/PrintVisitor.java | 171 +--
.../ir/visitor/NodeOperatorVisitor.java | 219 ++-
.../internal/ir/visitor/NodeVisitor.java | 260 ++--
.../internal/lookup/MethodHandleFactory.java | 12 +-
.../internal/objects/NativeString.java | 1 -
.../internal/parser/AbstractParser.java | 26 +-
.../nashorn/internal/parser/JSONParser.java | 19 +-
.../jdk/nashorn/internal/parser/Parser.java | 950 +++++--------
.../nashorn/internal/parser/TokenType.java | 5 +
.../jdk/nashorn/internal/runtime/Context.java | 16 +-
.../nashorn/internal/runtime/DebugLogger.java | 102 +-
.../RecompilableScriptFunctionData.java | 20 +-
.../internal/runtime/StructureLoader.java | 4 +-
.../runtime/linker/ClassAndLoader.java | 2 +-
nashorn/src/jdk/nashorn/tools/Shell.java | 10 +
nashorn/test/script/basic/try2.js | 49 +
nashorn/test/script/basic/try2.js.EXPECTED | 8 +
82 files changed, 5571 insertions(+), 6602 deletions(-)
delete mode 100644 nashorn/src/jdk/nashorn/internal/codegen/Frame.java
create mode 100644 nashorn/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
create mode 100644 nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
create mode 100644 nashorn/src/jdk/nashorn/internal/ir/Flags.java
delete mode 100644 nashorn/src/jdk/nashorn/internal/ir/LabeledNode.java
rename nashorn/src/jdk/nashorn/internal/ir/{DoWhileNode.java => LexicalContextNode.java} (60%)
create mode 100644 nashorn/src/jdk/nashorn/internal/ir/LoopNode.java
create mode 100644 nashorn/src/jdk/nashorn/internal/ir/annotations/Immutable.java
create mode 100644 nashorn/test/script/basic/try2.js
create mode 100644 nashorn/test/script/basic/try2.js.EXPECTED
diff --git a/nashorn/bin/verbose_octane.sh b/nashorn/bin/verbose_octane.sh
index e70e9d48f52..1895afed146 100644
--- a/nashorn/bin/verbose_octane.sh
+++ b/nashorn/bin/verbose_octane.sh
@@ -26,7 +26,7 @@ if [ -z $ITERS ]; then
ITERS=7
fi
NASHORN_JAR=dist/nashorn.jar
-JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
+JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
OCTANE_ARGS="--verbose --iterations ${ITERS}"
diff --git a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index 55967bb04d4..197a6da472f 100644
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -397,10 +397,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
setContextVariables(ctxt);
- final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
- final String fileName = (val != null) ? val.toString() : "";
- Object res = ScriptRuntime.apply(script, ctxtGlobal);
- return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
+ return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
index 9ecf7c89a9c..373bc517c89 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
@@ -25,14 +25,16 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
@@ -42,18 +44,20 @@ import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
+import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -62,6 +66,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -86,7 +91,6 @@ import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
@@ -105,21 +109,24 @@ import jdk.nashorn.internal.runtime.ScriptObject;
*/
final class Attr extends NodeOperatorVisitor {
+
/**
* Local definitions in current block (to discriminate from function
* declarations always defined in the function scope. This is for
* "can be undefined" analysis.
*/
- private Set localDefs;
+ private final Deque> localDefs;
/**
* Local definitions in current block to guard against cases like
* NASHORN-467 when things can be undefined as they are used before
* their local var definition. *sigh* JavaScript...
*/
- private Set localUses;
+ private final Deque> localUses;
- private final LexicalContext lexicalContext = new LexicalContext();
+ private final Deque returnTypes;
+
+ private final Map selfSymbolToFunction = new IdentityHashMap<>();
private static final DebugLogger LOG = new DebugLogger("attr");
private static final boolean DEBUG = LOG.isEnabled();
@@ -128,10 +135,13 @@ final class Attr extends NodeOperatorVisitor {
* Constructor.
*/
Attr() {
+ localDefs = new ArrayDeque<>();
+ localUses = new ArrayDeque<>();
+ returnTypes = new ArrayDeque<>();
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
return start(node);
}
@@ -142,217 +152,44 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
- newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
+ ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
end(accessNode);
return accessNode;
}
- @Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
- start(block);
+ private void enterFunctionBody() {
- final Set savedLocalDefs = localDefs;
- final Set savedLocalUses = localUses;
-
- block.setFrame(getCurrentFunctionNode().pushFrame());
-
- try {
- // a block starts out by copying the local defs and local uses
- // from the outer level. But we need the copies, as when we
- // leave the block the def and use sets given upon entry must
- // be restored
- localDefs = new HashSet<>(savedLocalDefs);
- localUses = new HashSet<>(savedLocalUses);
-
- block.visitStatements(this);
- } finally {
- localDefs = savedLocalDefs;
- localUses = savedLocalUses;
-
- getCurrentFunctionNode().popFrame();
- }
-
- end(block);
-
- lexicalContext.pop(block);
- return null;
- }
-
- @Override
- public Node enterCallNode(final CallNode callNode) {
- start(callNode);
-
- callNode.getFunction().accept(this);
-
- final List acceptedArgs = new ArrayList<>(callNode.getArgs().size());
- for (final Node arg : callNode.getArgs()) {
- LOG.info("Doing call arg " + arg);
- acceptedArgs.add(arg.accept(this));
- }
- callNode.setArgs(acceptedArgs);
-
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
-
- final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode());
- assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it
- evalArgs.setThis(thisNode);
- }
-
- newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
-
- end(callNode);
-
- return null;
- }
-
- @Override
- public Node enterCatchNode(final CatchNode catchNode) {
- final IdentNode exception = catchNode.getException();
- final Block block = getCurrentBlock();
-
- start(catchNode);
-
- // define block-local exception variable
- final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
- newType(def, Type.OBJECT);
- addLocalDef(exception.getName());
-
- return catchNode;
- }
-
- /**
- * Declare the definition of a new symbol.
- *
- * @param name Name of symbol.
- * @param symbolFlags Symbol flags.
- * @param node Defining Node.
- *
- * @return Symbol for given name or null for redefinition.
- */
- private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
- int flags = symbolFlags;
- Symbol symbol = findSymbol(block, name); // Locate symbol.
-
- if ((flags & KINDMASK) == IS_GLOBAL) {
- flags |= IS_SCOPE;
- }
-
- final FunctionNode function = lexicalContext.getFunction(block);
- if (symbol != null) {
- // Symbol was already defined. Check if it needs to be redefined.
- if ((flags & KINDMASK) == IS_PARAM) {
- if (!isLocal(function, symbol)) {
- // Not defined in this function. Create a new definition.
- symbol = null;
- } else if (symbol.isParam()) {
- // Duplicate parameter. Null return will force an error.
- assert false : "duplicate parameter";
- return null;
- }
- } else if ((flags & KINDMASK) == IS_VAR) {
- if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
- assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
- // Always create a new definition.
- symbol = null;
- } else {
- // Not defined in this function. Create a new definition.
- if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
- symbol = null;
- }
- }
- }
- }
-
- if (symbol == null) {
- // If not found, then create a new one.
- Block symbolBlock;
-
- // Determine where to create it.
- if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
- symbolBlock = block;
- } else {
- symbolBlock = function;
- }
-
- // Create and add to appropriate block.
- symbol = new Symbol(name, flags, node, symbolBlock);
- symbolBlock.putSymbol(name, symbol);
-
- if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
- symbolBlock.getFrame().addSymbol(symbol);
- symbol.setNeedsSlot(true);
- }
- } else if (symbol.less(flags)) {
- symbol.setFlags(flags);
- }
-
- if (node != null) {
- node.setSymbol(symbol);
- }
-
- return symbol;
- }
-
- @Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- start(functionNode, false);
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
- newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
- functionNode.setReturnType(Type.OBJECT);
- end(functionNode);
- return null;
- }
-
- lexicalContext.push(functionNode);
-
- clearLocalDefs();
- clearLocalUses();
-
- functionNode.setFrame(functionNode.pushFrame());
-
- initCallee(functionNode);
- initThis(functionNode);
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final Block body = getLexicalContext().getCurrentBlock();
+ initCallee(body);
+ initThis(body);
if (functionNode.isVarArg()) {
- initVarArg(functionNode);
+ initVarArg(body, functionNode.needsArguments());
}
- initParameters(functionNode);
- initScope(functionNode);
- initReturn(functionNode);
-
- // Add all nested declared functions as symbols in this function
- for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
- final IdentNode ident = nestedFunction.getIdent();
- if (ident != null) {
- assert nestedFunction.isDeclared();
- final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
- newType(functionSymbol, Type.typeFor(ScriptFunction.class));
- }
- }
+ initParameters(functionNode, body);
+ initScope(body);
+ initReturn(body);
if (functionNode.isProgram()) {
- initFromPropertyMap(functionNode);
+ initFromPropertyMap(body);
}
// Add function name as local symbol
if (!functionNode.isDeclared() && !functionNode.isProgram()) {
- if(functionNode.getSymbol() != null) {
+ if (functionNode.getSymbol() != null) {
// a temporary left over from an earlier pass when the function was lazy
assert functionNode.getSymbol().isTemp();
// remove it
functionNode.setSymbol(null);
}
final Symbol selfSymbol;
- if(functionNode.isAnonymous()) {
- selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
+ if (functionNode.isAnonymous()) {
+ selfSymbol = ensureSymbol(functionNode, Type.OBJECT, functionNode);
} else {
- selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
+ selfSymbol = defineSymbol(body, functionNode.getIdent().getName(), IS_VAR | IS_FUNCTION_SELF, functionNode);
newType(selfSymbol, Type.OBJECT);
- selfSymbol.setNode(functionNode);
+ selfSymbolToFunction.put(selfSymbol, functionNode);
}
}
@@ -373,73 +210,243 @@ final class Attr extends NodeOperatorVisitor {
* @see NASHORN-73
*/
- final List declaredSymbols = new ArrayList<>();
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
// in a separate step above) and "var" declarations in for loop initializers.
- functionNode.accept(new NodeOperatorVisitor() {
+ body.accept(new NodeOperatorVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode nestedFn) {
- // Don't descend into nested functions
- return nestedFn == functionNode ? nestedFn : null;
+ public boolean enterFunctionNode(final FunctionNode nestedFn) {
+ return false;
}
+
@Override
- public Node enterVarNode(VarNode varNode) {
- if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
+ public boolean enterVarNode(final VarNode varNode) {
+
+ // any declared symbols that aren't visited need to be typed as well, hence the list
+
+ if (varNode.isStatement()) {
+
final IdentNode ident = varNode.getName();
- // any declared symbols that aren't visited need to be typed as well, hence the list
- declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
+ final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
+ functionNode.addDeclaredSymbol(symbol);
+ if (varNode.isFunctionDeclaration()) {
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ }
}
- return null;
+ return false;
}
});
+ }
- visitFunctionStatements(functionNode);
+ @Override
+ public boolean enterBlock(final Block block) {
+ start(block);
+
+ if (getLexicalContext().isFunctionBody()) {
+ enterFunctionBody();
+ }
+ pushLocalsBlock();
+
+ return true;
+ }
+
+ @Override
+ public Node leaveBlock(final Block block) {
+ popLocals();
+ return end(block);
+ }
+
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ ensureSymbol(callNode.getType(), callNode);
+ return end(callNode);
+ }
+
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ return start(callNode);
+ }
+
+ @Override
+ public boolean enterCatchNode(final CatchNode catchNode) {
+ final IdentNode exception = catchNode.getException();
+ final Block block = getLexicalContext().getCurrentBlock();
+
+ start(catchNode);
+
+ // define block-local exception variable
+ final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
+ newType(def, Type.OBJECT);
+ addLocalDef(exception.getName());
+
+ return true;
+ }
+
+ /**
+ * Declare the definition of a new symbol.
+ *
+ * @param name Name of symbol.
+ * @param symbolFlags Symbol flags.
+ * @param node Defining Node.
+ *
+ * @return Symbol for given name or null for redefinition.
+ */
+ private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
+ int flags = symbolFlags;
+ Symbol symbol = findSymbol(block, name); // Locate symbol.
+
+ if ((flags & KINDMASK) == IS_GLOBAL) {
+ flags |= IS_SCOPE;
+ }
+
+ final FunctionNode function = getLexicalContext().getFunction(block);
+ if (symbol != null) {
+ // Symbol was already defined. Check if it needs to be redefined.
+ if ((flags & KINDMASK) == IS_PARAM) {
+ if (!isLocal(function, symbol)) {
+ // Not defined in this function. Create a new definition.
+ symbol = null;
+ } else if (symbol.isParam()) {
+ // Duplicate parameter. Null return will force an error.
+ assert false : "duplicate parameter";
+ return null;
+ }
+ } else if ((flags & KINDMASK) == IS_VAR) {
+ if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
+ // Always create a new definition.
+ symbol = null;
+ } else {
+ // Not defined in this function. Create a new definition.
+ if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
+ symbol = null;
+ }
+ }
+ }
+ }
+
+ if (symbol == null) {
+ // If not found, then create a new one.
+ Block symbolBlock;
+
+ // Determine where to create it.
+ if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
+ symbolBlock = block; //internal vars are always defined in the block closest to them
+ } else {
+ symbolBlock = getLexicalContext().getFunctionBody(function);
+ }
+
+ // Create and add to appropriate block.
+ symbol = new Symbol(name, flags);
+ symbolBlock.putSymbol(name, symbol);
+
+ if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
+ symbol.setNeedsSlot(true);
+ }
+ } else if (symbol.less(flags)) {
+ symbol.setFlags(flags);
+ }
+
+ if (node != null) {
+ node.setSymbol(symbol);
+ }
+
+ return symbol;
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ start(functionNode, false);
+
+ if (functionNode.isDeclared()) {
+ final Iterator blocks = getLexicalContext().getBlocks();
+ if (blocks.hasNext()) {
+ defineSymbol(
+ blocks.next(),
+ functionNode.getIdent().getName(),
+ IS_VAR,
+ functionNode);
+ } else {
+ // Q: What's an outermost function in a lexical context that is not a program?
+ // A: It's a function being compiled lazily!
+ assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
+ }
+ }
+
+ if (functionNode.isLazy()) {
+ LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
+ ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
+ end(functionNode);
+ return false;
+ }
+
+ returnTypes.push(functionNode.getReturnType());
+ pushLocalsFunction();
+ return true;
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ FunctionNode newFunctionNode = functionNode;
+
+ final LexicalContext lc = getLexicalContext();
//unknown parameters are promoted to object type.
- finalizeParameters(functionNode);
- finalizeTypes(functionNode);
- for (final Symbol symbol : declaredSymbols) {
+ finalizeParameters(newFunctionNode);
+ finalizeTypes(newFunctionNode);
+ for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
if (symbol.getSymbolType().isUnknown()) {
symbol.setType(Type.OBJECT);
symbol.setCanBeUndefined();
}
}
- if (functionNode.getReturnType().isUnknown()) {
- LOG.info("Unknown return type promoted to object");
- functionNode.setReturnType(Type.OBJECT);
+ final Block body = newFunctionNode.getBody();
+
+ if (newFunctionNode.hasLazyChildren()) {
+ //the final body has already been assigned as we have left the function node block body by now
+ objectifySymbols(body);
}
- if (functionNode.getSelfSymbolInit() != null) {
- LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName());
- final Node init = functionNode.getSelfSymbolInit();
+ if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
+ final IdentNode callee = compilerConstant(CALLEE);
+ final VarNode selfInit =
+ new VarNode(
+ newFunctionNode.getSource(),
+ newFunctionNode.getToken(),
+ newFunctionNode.getFinish(),
+ newFunctionNode.getIdent(),
+ callee);
+
+ LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
+
final List newStatements = new ArrayList<>();
- newStatements.add(init);
- newStatements.addAll(functionNode.getStatements());
- functionNode.setStatements(newStatements);
- functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
+ newStatements.add(selfInit);
+ assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
+
+ final IdentNode name = selfInit.getName();
+ final Symbol nameSymbol = body.getExistingSymbol(name.getName());
+
+ assert nameSymbol != null;
+
+ name.setSymbol(nameSymbol);
+ selfInit.setSymbol(nameSymbol);
+
+ newStatements.addAll(body.getStatements());
+ newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
}
- if (functionNode.hasLazyChildren()) {
- objectifySymbols(functionNode);
+ if (returnTypes.peek().isUnknown()) {
+ LOG.info("Unknown return type promoted to object");
+ newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
}
+ final Type returnType = returnTypes.pop();
+ newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
+ newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
- functionNode.popFrame();
+ popLocals();
- functionNode.setState(CompilationState.ATTR);
+ end(newFunctionNode, false);
- end(functionNode, false);
- lexicalContext.pop(functionNode);
-
- return null;
- }
-
- private void visitFunctionStatements(final FunctionNode functionNode) {
- final List newStatements = new ArrayList<>(functionNode.getStatements());
- for(ListIterator stmts = newStatements.listIterator(); stmts.hasNext();) {
- stmts.set(stmts.next().accept(this));
- }
- functionNode.setStatements(newStatements);
+ return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
}
@Override
@@ -450,7 +457,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
start(identNode);
@@ -458,31 +465,28 @@ final class Attr extends NodeOperatorVisitor {
if (identNode.isPropertyName()) {
// assign a pseudo symbol to property name
final Symbol pseudoSymbol = pseudoSymbol(name);
- LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol);
+ LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
LOG.unindent();
identNode.setSymbol(pseudoSymbol);
- return null;
+ return false;
}
- final Block block = getCurrentBlock();
- final Symbol oldSymbol = identNode.getSymbol();
+ final LexicalContext lc = getLexicalContext();
+ final Block block = lc.getCurrentBlock();
+ final Symbol oldSymbol = identNode.getSymbol();
Symbol symbol = findSymbol(block, name);
//If an existing symbol with the name is found, use that otherwise, declare a new one
if (symbol != null) {
- LOG.info("Existing symbol = " + symbol);
- if (isFunctionExpressionSelfReference(symbol)) {
- final FunctionNode functionNode = (FunctionNode)symbol.getNode();
- assert functionNode.getCalleeNode() != null;
-
- final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
- //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
-
- functionNode.setNeedsSelfSymbol(var);
- }
-
- if (!identNode.isInitializedHere()) { // NASHORN-448
+ LOG.info("Existing symbol = ", symbol);
+ if (symbol.isFunctionSelf()) {
+ final FunctionNode functionNode = lc.getDefiningFunction(symbol);
+ assert functionNode != null;
+ assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
+ lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ } else if (!identNode.isInitializedHere()) { // NASHORN-448
// here is a use outside the local def scope
if (!isLocalDef(name)) {
newType(symbol, Type.OBJECT);
@@ -492,25 +496,19 @@ final class Attr extends NodeOperatorVisitor {
identNode.setSymbol(symbol);
// non-local: we need to put symbol in scope (if it isn't already)
- if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
- symbol.setIsScope();
+ if (!isLocal(lc.getCurrentFunction(), symbol) && !symbol.isScope()) {
+ Symbol.setSymbolIsScope(lc, symbol);
}
} else {
- LOG.info("No symbol exists. Declare undefined: " + symbol);
- symbol = useSymbol(block, name, identNode);
+ LOG.info("No symbol exists. Declare undefined: ", symbol);
+ symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
// we have never seen this before, it can be undefined
newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
symbol.setCanBeUndefined();
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
}
- assert symbol != null;
- if(symbol.isGlobal()) {
- setUsesGlobalSymbol();
- } else if(symbol.isScope()) {
- final Iterator blocks = lexicalContext.getBlocks();
- blocks.next().setUsesScopeSymbol(symbol, blocks);
- }
+ setBlockScope(name, symbol);
if (symbol != oldSymbol && !identNode.isInitializedHere()) {
symbol.increaseUseCount();
@@ -519,7 +517,37 @@ final class Attr extends NodeOperatorVisitor {
end(identNode);
- return null;
+ return false;
+ }
+
+ private void setBlockScope(final String name, final Symbol symbol) {
+ assert symbol != null;
+ if (symbol.isGlobal()) {
+ setUsesGlobalSymbol();
+ return;
+ }
+
+ if (symbol.isScope()) {
+ final LexicalContext lc = getLexicalContext();
+
+ Block scopeBlock = null;
+ for (final Iterator contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) {
+ final LexicalContextNode node = contextNodeIter.next();
+ if (node instanceof Block) {
+ if (((Block)node).getExistingSymbol(name) != null) {
+ scopeBlock = (Block)node;
+ break;
+ }
+ } else if (node instanceof FunctionNode) {
+ lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
+
+ if (scopeBlock != null) {
+ assert getLexicalContext().contains(scopeBlock);
+ lc.setFlag(scopeBlock, Block.NEEDS_SCOPE);
+ }
+ }
}
/**
@@ -528,34 +556,11 @@ final class Attr extends NodeOperatorVisitor {
* @see #needsParentScope()
*/
private void setUsesGlobalSymbol() {
- for(final Iterator fns = lexicalContext.getFunctions(); fns.hasNext();) {
- fns.next().setUsesAncestorScope();
+ for (final Iterator fns = getLexicalContext().getFunctions(); fns.hasNext();) {
+ getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
}
}
- /**
- * Declare the use of a symbol in a block.
- *
- * @param block block in which the symbol is used
- * @param name Name of symbol.
- * @param node Using node
- *
- * @return Symbol for given name.
- */
- private Symbol useSymbol(final Block block, final String name, final Node node) {
- Symbol symbol = findSymbol(block, name);
-
- if (symbol == null) {
- // If not found, declare as a free var.
- symbol = defineSymbol(block, name, IS_GLOBAL, node);
- } else {
- node.setSymbol(symbol);
- }
-
- return symbol;
- }
-
-
/**
* Search for symbol in the lexical context starting from the given block.
* @param name Symbol name.
@@ -564,7 +569,7 @@ final class Attr extends NodeOperatorVisitor {
private Symbol findSymbol(final Block block, final String name) {
// Search up block chain to locate symbol.
- for(final Iterator blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+ for(final Iterator blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
// Find name.
final Symbol symbol = blocks.next().getExistingSymbol(name);
// If found then we are good.
@@ -577,13 +582,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
- newTemporary(Type.OBJECT, indexNode); //TODO
+ ensureSymbol(Type.OBJECT, indexNode); //TODO
return indexNode;
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
try {
start(literalNode);
assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
@@ -604,26 +609,33 @@ final class Attr extends NodeOperatorVisitor {
assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
}
- getCurrentFunctionNode().newLiteral(literalNode);
+ getLexicalContext().getCurrentFunction().newLiteral(literalNode);
} finally {
end(literalNode);
}
- return null;
+
+ return false;
+ }
+
+ @Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ return start(objectNode);
}
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
- newTemporary(Type.OBJECT, objectNode);
- end(objectNode);
- return objectNode;
+ ensureSymbol(Type.OBJECT, objectNode);
+ return end(objectNode);
}
+ //TODO is this correct why not leave?
@Override
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
// assign a pseudo symbol to property name, see NASHORN-710
+ start(propertyNode);
propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
end(propertyNode);
- return propertyNode;
+ return true;
}
@Override
@@ -636,8 +648,10 @@ final class Attr extends NodeOperatorVisitor {
if (expr.getType().isUnknown() && symbol.isParam()) {
symbol.setType(Type.OBJECT);
}
- getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType()));
- LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType());
+
+ final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType());
+ returnTypes.push(returnType);
+ LOG.info("Returntype is now ", returnType);
}
end(returnNode);
@@ -649,25 +663,29 @@ final class Attr extends NodeOperatorVisitor {
public Node leaveSwitchNode(final SwitchNode switchNode) {
Type type = Type.UNKNOWN;
+ final List newCases = new ArrayList<>();
for (final CaseNode caseNode : switchNode.getCases()) {
final Node test = caseNode.getTest();
+
+ CaseNode newCaseNode = caseNode;
if (test != null) {
if (test instanceof LiteralNode) {
//go down to integers if we can
final LiteralNode> lit = (LiteralNode>)test;
if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
if (JSType.isRepresentableAsInt(lit.getNumber())) {
- caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
+ newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
}
}
} else {
// the "all integer" case that CodeGenerator optimizes for currently assumes literals only
type = Type.OBJECT;
- break;
}
- type = Type.widest(type, caseNode.getTest().getType());
+ type = Type.widest(type, newCaseNode.getTest().getType());
}
+
+ newCases.add(newCaseNode);
}
//only optimize for all integers
@@ -675,11 +693,11 @@ final class Attr extends NodeOperatorVisitor {
type = Type.OBJECT;
}
- switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+ switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
end(switchNode);
- return switchNode;
+ return switchNode.setCases(getLexicalContext(), newCases);
}
@Override
@@ -696,25 +714,25 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
start(varNode);
final IdentNode ident = varNode.getName();
final String name = ident.getName();
- final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
+ final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
assert symbol != null;
- LOG.info("VarNode " + varNode + " set symbol " + symbol);
+ LOG.info("VarNode ", varNode, " set symbol ", symbol);
varNode.setSymbol(symbol);
// NASHORN-467 - use before definition of vars - conservative
- if (localUses.contains(ident.getName())) {
+ if (isLocalUse(ident.getName())) {
newType(symbol, Type.OBJECT);
symbol.setCanBeUndefined();
}
- return varNode;
+ return true;
}
@Override
@@ -734,7 +752,7 @@ final class Attr extends NodeOperatorVisitor {
addLocalDef(name);
final Symbol symbol = varNode.getSymbol();
- final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
+ final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
// Forbid integers as local vars for now as we have no way to treat them as undefined
newType(symbol, init.getType());
@@ -751,14 +769,14 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveADD(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- newTemporary(Type.INT, unaryNode);
+ ensureSymbol(Type.INT, unaryNode);
end(unaryNode);
return unaryNode;
}
@@ -766,30 +784,29 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
// @see assignOffset
- ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
final Type type = arithType();
newType(unaryNode.rhs().getSymbol(), type);
- newTemporary(type, unaryNode);
+ ensureSymbol(type, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = getCurrentFunctionNode();
- final boolean strictMode = currentFunctionNode.isStrictMode();
+ final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction();
+ final boolean strictMode = currentFunctionNode.isStrict();
final Node rhs = unaryNode.rhs();
final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
- final RuntimeNode runtimeNode;
final List args = new ArrayList<>();
if (rhs instanceof IdentNode) {
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ((IdentNode)rhs).getName();
- final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel());
+ final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
if (failDelete && rhs.getSymbol().isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
@@ -797,7 +814,7 @@ final class Attr extends NodeOperatorVisitor {
final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
if (!failDelete) {
- args.add(currentFunctionNode.getScopeNode());
+ args.add(compilerConstant(SCOPE));
}
args.add(literalNode);
args.add(strictFlagNode);
@@ -825,42 +842,62 @@ final class Attr extends NodeOperatorVisitor {
return LiteralNode.newInstance(unaryNode, true).accept(this);
}
- runtimeNode = new RuntimeNode(unaryNode, request, args);
- assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
+ final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
+ assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
return leaveRuntimeNode(runtimeNode);
}
+ /**
+ * Is the symbol denoted by the specified name in the current lexical context defined in the program level
+ * @param name the name of the symbol
+ * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
+ */
+ private boolean isProgramLevelSymbol(final String name) {
+ for(final Iterator it = getLexicalContext().getBlocks(); it.hasNext();) {
+ final Block next = it.next();
+ if(next.getExistingSymbol(name) != null) {
+ return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction());
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
- newTemporary(Type.OBJECT, unaryNode);
+ ensureSymbol(Type.OBJECT, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
- newTemporary(Type.BOOLEAN, unaryNode);
+ ensureSymbol(Type.BOOLEAN, unaryNode);
end(unaryNode);
return unaryNode;
}
+ private IdentNode compilerConstant(CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
+ node.setSymbol(functionNode.compilerConstant(cc));
+ return node;
+ }
+
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
- final Node rhs = unaryNode.rhs();
-
- RuntimeNode runtimeNode;
+ final Node rhs = unaryNode.rhs();
List args = new ArrayList<>();
if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
- args.add(getCurrentFunctionNode().getScopeNode());
+ args.add(compilerConstant(SCOPE));
args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
} else {
args.add(rhs);
args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
}
- runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
+ RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
assert runtimeNode.getSymbol() == unaryNode.getSymbol();
runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
@@ -872,21 +909,20 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
+ ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
return runtimeNode;
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveVOID(final UnaryNode unaryNode) {
- final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID);
- runtimeNode.accept(this);
+ final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this);
assert runtimeNode.getSymbol().getSymbolType().isObject();
end(unaryNode);
return runtimeNode;
@@ -903,7 +939,7 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
- newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
+ ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
end(binaryNode);
@@ -912,7 +948,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAND(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -921,39 +957,40 @@ final class Attr extends NodeOperatorVisitor {
* This is a helper called before an assignment.
* @param binaryNode assignment node
*/
- private Node enterAssignmentNode(final BinaryNode binaryNode) {
+ private boolean enterAssignmentNode(final BinaryNode binaryNode) {
start(binaryNode);
final Node lhs = binaryNode.lhs();
if (lhs instanceof IdentNode) {
- final Block block = getCurrentBlock();
- final IdentNode ident = (IdentNode)lhs;
- final String name = ident.getName();
+ final LexicalContext lc = getLexicalContext();
+ final Block block = lc.getCurrentBlock();
+ final IdentNode ident = (IdentNode)lhs;
+ final String name = ident.getName();
- Symbol symbol = findSymbol(getCurrentBlock(), name);
+ Symbol symbol = findSymbol(block, name);
if (symbol == null) {
symbol = defineSymbol(block, name, IS_GLOBAL, ident);
binaryNode.setSymbol(symbol);
- } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
- symbol.setIsScope();
+ } else if (!isLocal(lc.getCurrentFunction(), symbol)) {
+ Symbol.setSymbolIsScope(lc, symbol);
}
addLocalDef(name);
}
- return binaryNode;
+ return true;
}
private boolean isLocal(FunctionNode function, Symbol symbol) {
- final Block block = symbol.getBlock();
- // some temp symbols have no block, so can be assumed local
- return block == null || lexicalContext.getFunction(block) == function;
+ final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
+ // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
+ return definingFn == null || definingFn == function;
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -963,7 +1000,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -978,7 +1015,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -988,7 +1025,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -998,7 +1035,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1008,7 +1045,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1018,7 +1055,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1028,7 +1065,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1038,7 +1075,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1048,7 +1085,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1058,7 +1095,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1068,7 +1105,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1094,13 +1131,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.rhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.rhs().getType(), binaryNode);
return binaryNode;
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.lhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.lhs().getType(), binaryNode);
return binaryNode;
}
@@ -1113,7 +1150,7 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
- newTemporary(Type.BOOLEAN, binaryNode);
+ ensureSymbol(Type.BOOLEAN, binaryNode);
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
@@ -1131,7 +1168,7 @@ final class Attr extends NodeOperatorVisitor {
//newType(binaryNode.lhs().getSymbol(), operandType);
//newType(binaryNode.rhs().getSymbol(), operandType);
- newTemporary(destType, binaryNode);
+ ensureSymbol(destType, binaryNode);
return binaryNode;
}
@@ -1216,7 +1253,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveOR(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -1244,7 +1281,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
- forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+ forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
/*
* Iterators return objects, so we need to widen the scope of the
* init variable if it, for example, has been assigned double type
@@ -1267,65 +1304,50 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(rhs);
final Type type = Type.widest(lhs.getType(), rhs.getType());
- newTemporary(type, ternaryNode);
+ ensureSymbol(type, ternaryNode);
end(ternaryNode);
+ assert ternaryNode.getSymbol() != null;
return ternaryNode;
}
- private void initThis(final FunctionNode functionNode) {
- final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
+ private void initThis(final Block block) {
+ final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
newType(thisSymbol, Type.OBJECT);
thisSymbol.setNeedsSlot(true);
- functionNode.getThisNode().setSymbol(thisSymbol);
- LOG.info("Initialized scope symbol: " + thisSymbol);
}
- private void initScope(final FunctionNode functionNode) {
- final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initScope(final Block block) {
+ final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(scopeSymbol, Type.typeFor(ScriptObject.class));
scopeSymbol.setNeedsSlot(true);
- functionNode.getScopeNode().setSymbol(scopeSymbol);
- LOG.info("Initialized scope symbol: " + scopeSymbol);
}
- private void initReturn(final FunctionNode functionNode) {
- final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initReturn(final Block block) {
+ final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(returnSymbol, Type.OBJECT);
returnSymbol.setNeedsSlot(true);
- functionNode.getResultNode().setSymbol(returnSymbol);
- LOG.info("Initialized return symbol: " + returnSymbol);
//return symbol is always object as it's the __return__ thing. What returnType is is another matter though
}
- private void initVarArg(final FunctionNode functionNode) {
- if (functionNode.isVarArg()) {
- final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
- varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
- varArgsSymbol.setNeedsSlot(true);
- functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
- LOG.info("Initialized varargs symbol: " + varArgsSymbol);
+ private void initVarArg(final Block block, final boolean needsArguments) {
+ final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
+ varArgsSymbol.setNeedsSlot(true);
- if (functionNode.needsArguments()) {
- final String argumentsName = functionNode.getArgumentsNode().getName();
- final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
- newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
- argumentsSymbol.setNeedsSlot(true);
- functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
- addLocalDef(argumentsName);
- LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
- }
+ if (needsArguments) {
+ final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
+ newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
+ argumentsSymbol.setNeedsSlot(true);
+ addLocalDef(ARGUMENTS.symbolName());
}
}
- private void initCallee(final FunctionNode functionNode) {
- assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
- final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
- newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
+ private void initCallee(final Block block) {
+ final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
calleeSymbol.setNeedsSlot(true);
- functionNode.getCalleeNode().setSymbol(calleeSymbol);
- LOG.info("Initialized callee symbol " + calleeSymbol);
}
/**
@@ -1334,25 +1356,19 @@ final class Attr extends NodeOperatorVisitor {
*
* @param functionNode the function node
*/
- private void initParameters(final FunctionNode functionNode) {
- //If a function is specialized, we don't need to tag either it return
- // type or its parameters with the widest (OBJECT) type for safety.
- functionNode.setReturnType(Type.UNKNOWN);
-
+ private void initParameters(final FunctionNode functionNode, final Block body) {
for (final IdentNode param : functionNode.getParameters()) {
addLocalDef(param.getName());
- final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
+ final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
if (paramSymbol != null) {
final Type callSiteParamType = functionNode.getSpecializedType(param);
if (callSiteParamType != null) {
- LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
-
- System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+ LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
}
newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
}
- LOG.info("Initialized param " + paramSymbol);
+ LOG.info("Initialized param ", paramSymbol);
}
}
@@ -1378,7 +1394,7 @@ final class Attr extends NodeOperatorVisitor {
// this function, we can tell the runtime system that no matter what the
// call site is, use this information. TODO
if (!paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+ LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
}
newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@@ -1392,19 +1408,18 @@ final class Attr extends NodeOperatorVisitor {
/**
* Move any properties from a global map into the scope of this method
- * @param functionNode the function node for which to init scope vars
+ * @param block the function node body for which to init scope vars
*/
- private void initFromPropertyMap(final FunctionNode functionNode) {
+ private void initFromPropertyMap(final Block block) {
// For a script, add scope symbols as defined in the property map
- assert functionNode.isProgram();
final PropertyMap map = Context.getGlobalMap();
for (final Property property : map.getProperties()) {
final String key = property.getKey();
- final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
+ final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
newType(symbol, Type.OBJECT);
- LOG.info("Added global symbol from property map " + symbol);
+ LOG.info("Added global symbol from property map ", symbol);
}
}
@@ -1412,7 +1427,7 @@ final class Attr extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol();
- LOG.info("Ensure type not unknown for: " + symbol);
+ LOG.info("Ensure type not unknown for: ", symbol);
/*
* Note that not just unknowns, but params need to be blown
@@ -1452,7 +1467,7 @@ final class Attr extends NodeOperatorVisitor {
}
private Symbol exceptionSymbol() {
- return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+ return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
}
/**
@@ -1512,15 +1527,15 @@ final class Attr extends NodeOperatorVisitor {
}
Type from = node.getType();
if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
- LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to);
+ LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
newType(node.getSymbol(), to);
changed.add(node);
}
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- return node.isLazy() ? null : node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return !node.isLazy();
}
/**
@@ -1574,7 +1589,7 @@ final class Attr extends NodeOperatorVisitor {
} else {
type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
}
- newTemporary(type, binaryNode);
+ ensureSymbol(type, binaryNode);
newType(lhs.getSymbol(), type);
end(binaryNode);
return binaryNode;
@@ -1589,32 +1604,25 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
- newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+ ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
- ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode);
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
end(binaryNode);
return binaryNode;
}
- private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
- if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
- return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
- }
- return false;
+ private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
+ LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
+ return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
}
- private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) {
- LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type);
- return functionNode.newTemporary(type, node);
- }
-
- private Symbol newTemporary(final Type type, final Node node) {
- return newTemporary(getCurrentFunctionNode(), type, node);
+ private Symbol ensureSymbol(final Type type, final Node node) {
+ return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
}
private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
+ final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
iter.setType(type); // NASHORN-73
return iter;
}
@@ -1624,40 +1632,51 @@ final class Attr extends NodeOperatorVisitor {
symbol.setType(type);
if (symbol.getSymbolType() != oldType) {
- LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")");
+ LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
}
if (symbol.isParam()) {
symbol.setType(type);
- LOG.info("Param type change " + symbol);
+ LOG.info("Param type change ", symbol);
}
}
- private void clearLocalDefs() {
- localDefs = new HashSet<>();
+ private void pushLocalsFunction() {
+ localDefs.push(new HashSet());
+ localUses.push(new HashSet());
+ }
+
+ private void pushLocalsBlock() {
+ localDefs.push(localDefs.isEmpty() ? new HashSet() : new HashSet<>(localDefs.peek()));
+ localUses.push(localUses.isEmpty() ? new HashSet() : new HashSet<>(localUses.peek()));
+ }
+
+ private void popLocals() {
+ localDefs.pop();
+ localUses.pop();
}
private boolean isLocalDef(final String name) {
- return localDefs.contains(name);
+ return localDefs.peek().contains(name);
}
private void addLocalDef(final String name) {
- LOG.info("Adding local def of symbol: '" + name + "'");
- localDefs.add(name);
+ LOG.info("Adding local def of symbol: '", name, "'");
+ localDefs.peek().add(name);
}
private void removeLocalDef(final String name) {
- LOG.info("Removing local def of symbol: '" + name + "'");
- localDefs.remove(name);
+ LOG.info("Removing local def of symbol: '", name, "'");
+ localDefs.peek().remove(name);
}
- private void clearLocalUses() {
- localUses = new HashSet<>();
+ private boolean isLocalUse(final String name) {
+ return localUses.peek().contains(name);
}
private void addLocalUse(final String name) {
- LOG.info("Adding local use of symbol: '" + name + "'");
- localUses.add(name);
+ LOG.info("Adding local use of symbol: '", name, "'");
+ localUses.peek().add(name);
}
/**
@@ -1665,30 +1684,28 @@ final class Attr extends NodeOperatorVisitor {
* This is done when the function contains unevaluated black boxes such as
* lazy sub-function nodes that have not been compiled.
*
- * @param functionNode function node in whose scope symbols should conservatively be made objects
+ * @param body body for the function node we are leaving
*/
- private static void objectifySymbols(final FunctionNode functionNode) {
- functionNode.accept(new NodeVisitor() {
+ private static void objectifySymbols(final Block body) {
+ body.accept(new NodeVisitor() {
private void toObject(final Block block) {
for (final Iterator iter = block.symbolIterator(); iter.hasNext();) {
final Symbol symbol = iter.next();
- newType(symbol, Type.OBJECT);
+ if (!symbol.isTemp()) {
+ newType(symbol, Type.OBJECT);
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
toObject(block);
- return block;
+ return true;
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- toObject(node);
- if (node.isLazy()) {
- return null;
- }
- return node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return false;
}
});
}
@@ -1702,11 +1719,11 @@ final class Attr extends NodeOperatorVisitor {
return cn.substring(lastDot + 1);
}
- private Node start(final Node node) {
+ private boolean start(final Node node) {
return start(node, true);
}
- private Node start(final Node node, final boolean printNode) {
+ private boolean start(final Node node, final boolean printNode) {
if (DEBUG) {
final StringBuilder sb = new StringBuilder();
@@ -1715,13 +1732,13 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName()).
+ append(getLexicalContext().getCurrentFunction().getName()).
append("'");
- LOG.info(sb.toString());
+ LOG.info(sb);
LOG.indent();
}
- return node;
+ return true;
}
private Node end(final Node node) {
@@ -1737,7 +1754,7 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName());
+ append(getLexicalContext().getCurrentFunction().getName());
if (node.getSymbol() == null) {
sb.append(" ");
@@ -1746,7 +1763,7 @@ final class Attr extends NodeOperatorVisitor {
}
LOG.unindent();
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return node;
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 7ca7f994311..2d379183fd5 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -58,12 +58,14 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
+
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -219,14 +221,14 @@ public class ClassEmitter implements Emitter {
private void defineCommonStatics(final boolean strictMode) {
// source - used to store the source data (text) for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
// constants - used to the constants array for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
// strictMode - was this script compiled in strict mode. Set externally by the compiler.
- field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
+ field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
}
/**
@@ -238,9 +240,9 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(String.class)) {
// $getString - get the ith entry from the constants table and cast to String.
- final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
+ final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
getStringMethod.begin();
- getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(String.class)
@@ -250,7 +252,7 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(PropertyMap.class)) {
// $getMap - get the ith entry from the constants table and cast to PropertyMap.
- final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
+ final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
getMapMethod.begin();
getMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -260,7 +262,7 @@ public class ClassEmitter implements Emitter {
getMapMethod.end();
// $setMap - overwrite an existing map.
- final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
+ final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
setMapMethod.begin();
setMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -289,7 +291,7 @@ public class ClassEmitter implements Emitter {
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
getArrayMethod.begin();
- getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(cls)
@@ -307,7 +309,7 @@ public class ClassEmitter implements Emitter {
*/
static String getArrayMethodName(final Class> cls) {
assert cls.isArray();
- return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
+ return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
}
/**
@@ -409,6 +411,10 @@ public class ClassEmitter implements Emitter {
methodsStarted.remove(method);
}
+ SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class> rtype, final Class>... ptypes) {
+ return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
+ }
+
/**
* Add a new method to the class - defaults to public method
*
@@ -433,7 +439,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method
*/
MethodEmitter method(final EnumSet methodFlags, final String methodName, final Class> rtype, final Class>... ptypes) {
- return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
+ return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
}
/**
@@ -484,7 +490,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving
*/
MethodEmitter clinit() {
- return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
+ return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
}
/**
@@ -493,7 +499,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving ()V
*/
MethodEmitter init() {
- return method(INIT.tag(), void.class);
+ return method(INIT.symbolName(), void.class);
}
/**
@@ -503,7 +509,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving ()V
*/
MethodEmitter init(final Class>... ptypes) {
- return method(INIT.tag(), void.class, ptypes);
+ return method(INIT.symbolName(), void.class, ptypes);
}
/**
@@ -515,7 +521,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving (...)V
*/
MethodEmitter init(final EnumSet flags, final Class>... ptypes) {
- return method(flags, INIT.tag(), void.class, ptypes);
+ return method(flags, INIT.symbolName(), void.class, ptypes);
}
/**
@@ -628,4 +634,9 @@ public class ClassEmitter implements Emitter {
return v;
}
}
+
+ private MethodVisitor methodVisitor(EnumSet flags, final String methodName, final Class> rtype, final Class>... ptypes) {
+ return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
+ }
+
}
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index 1d09e9c98e0..15631ade26a 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -27,14 +27,18 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
-import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -48,8 +52,10 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -67,11 +73,11 @@ import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
+import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -85,6 +91,7 @@ import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
@@ -107,6 +114,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.RegexToken;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.Property;
@@ -157,14 +165,29 @@ final class CodeGenerator extends NodeOperatorVisitor {
/** How many regexp fields have been emitted */
private int regexFieldCount;
- /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
- * a just-defined anonymous function expression. */
- private boolean functionNodeIsCallee;
-
/** Map of shared scope call sites */
private final Map scopeCalls = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
+ /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque compileUnits = new ArrayDeque<>();
+
+ /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque methodEmitters = new ArrayDeque<>();
+
+ /** The discard stack - whenever we enter a discard node we keep track of its return value status -
+ * i.e. should we keep it or throw it away */
+ private final Deque discard = new ArrayDeque<>();
+
+ // A stack tracking the next free local variable slot in the blocks. There's one entry for every block
+ // currently on the lexical context stack.
+ private int[] nextFreeSlots = new int[16];
+ private int nextFreeSlotsSize = 0;
+
+ /** Current method emitter */
+ private MethodEmitter method;
+
+ /** Current compile unit */
+ private CompileUnit unit;
/** When should we stop caching regexp expressions in fields to limit bytecode size? */
private static final int MAX_REGEX_FIELDS = 2 * 1024;
@@ -188,7 +211,37 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return the correct flags for a call site in the current function
*/
int getCallSiteFlags() {
- return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ }
+
+ private void pushMethodEmitter(final MethodEmitter newMethod) {
+ methodEmitters.push(newMethod);
+ this.method = newMethod;
+ }
+
+ private void popMethodEmitter(final MethodEmitter oldMethod) {
+ assert methodEmitters.peek() == oldMethod;
+ methodEmitters.pop();
+ if (!methodEmitters.isEmpty()) {
+ this.method = methodEmitters.peek();
+ } else {
+ this.method = null;
+ }
+ }
+
+ private void push(final CompileUnit newUnit) {
+ compileUnits.push(newUnit);
+ this.unit = newUnit;
+ }
+
+ private void pop(final CompileUnit oldUnit) {
+ assert compileUnits.peek() == oldUnit;
+ compileUnits.pop();
+ if (!compileUnits.isEmpty()) {
+ this.unit = compileUnits.peek();
+ } else {
+ this.unit = null;
+ }
}
/**
@@ -217,7 +270,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
final int flags = CALLSITE_SCOPE | getCallSiteFlags();
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
@@ -237,11 +290,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return true if fast scope
*/
private boolean isFastScope(final Symbol symbol) {
- if (!symbol.isScope() || !symbol.getBlock().needsScope()) {
+ if (!symbol.isScope() || !getLexicalContext().getDefiningBlock(symbol).needsScope()) {
return false;
}
// Allow fast scope access if no function contains with or eval
- for(final Iterator it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
+ for (final Iterator it = getLexicalContext().getFunctions(); it.hasNext();) {
final FunctionNode func = it.next();
if (func.hasWith() || func.hasEval()) {
return false;
@@ -251,7 +304,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
- method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
+ method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1);
final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
scopeCall.generateInvoke(method);
return method;
@@ -271,10 +324,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
int depth = 0;
- final Block definingBlock = symbol.getBlock();
- for(final Iterator blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+ final String name = symbol.getName();
+ for(final Iterator blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) {
final Block currentBlock = blocks.next();
- if (currentBlock == definingBlock) {
+ if (currentBlock.getExistingSymbol(name) == symbol) {
return depth;
}
if (currentBlock.needsScope()) {
@@ -285,9 +338,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
- final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
+ final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol);
assert depth != -1;
- if(depth > 0) {
+ if (depth > 0) {
if (swap) {
method.swap();
}
@@ -328,46 +381,46 @@ final class CodeGenerator extends NodeOperatorVisitor {
*/
final CodeGenerator codegen = this;
- node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ node.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
loadIdent(identNode);
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
if (!baseAlreadyOnStack) {
load(accessNode.getBase()).convert(Type.OBJECT);
}
assert method.peekType().isObject();
method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
if (!baseAlreadyOnStack) {
load(indexNode.getBase()).convert(Type.OBJECT);
load(indexNode.getIndex());
}
method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
+ public boolean enterFunctionNode(FunctionNode functionNode) {
// function nodes will always leave a constructed function object on stack, no need to load the symbol
// separately as in enterDefault()
functionNode.accept(codegen);
- return null;
+ return false;
}
@Override
- public Node enterDefault(final Node otherNode) {
+ public boolean enterDefault(final Node otherNode) {
otherNode.accept(codegen); // generate code for whatever we are looking at.
method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
- return null;
+ return false;
}
});
@@ -375,14 +428,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
- if (accessNode.testResolved()) {
- return null;
- }
-
+ public boolean enterAccessNode(final AccessNode accessNode) {
load(accessNode);
-
- return null;
+ return false;
}
/**
@@ -407,7 +455,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
if (symbol.hasSlot() && !isInternal) {
- assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
+ assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction();
if (symbol.getSymbolType().isNumber()) {
numbers.add(symbol);
} else if (symbol.getSymbolType().isObject()) {
@@ -441,22 +489,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block containing symbols.
*/
private void symbolInfo(final Block block) {
- for (final Symbol symbol : block.getFrame().getSymbols()) {
- method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.hasSlot()) {
+ method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
- if (block.testResolved()) {
- return null;
- }
- lexicalContext.push(block);
-
+ public boolean enterBlock(final Block block) {
method.label(block.getEntryLabel());
initLocals(block);
- return block;
+ return true;
}
@Override
@@ -464,10 +510,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(block.getBreakLabel());
symbolInfo(block);
- if (block.needsScope()) {
+ if (block.needsScope() && !block.isTerminal()) {
popBlockScope(block);
}
- lexicalContext.pop(block);
+ --nextFreeSlotsSize;
return block;
}
@@ -477,34 +523,30 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Label skipLabel = new Label("skip_catch");
/* pop scope a la try-finally */
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method._goto(skipLabel);
method.label(exitLabel);
method._catch(recoveryLabel);
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method.athrow();
method.label(skipLabel);
method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- if (breakNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel());
- method.splitAwareGoto(breakNode.getTargetLabel());
-
- return null;
+ return false;
}
private int loadArgs(final List args) {
@@ -541,21 +583,17 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- if (callNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCallNode(final CallNode callNode) {
final List args = callNode.getArgs();
final Node function = callNode.getFunction();
- final Block currentBlock = getCurrentBlock();
+ final Block currentBlock = getLexicalContext().getCurrentBlock();
- function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ function.accept(new NodeVisitor() {
private void sharedScopeCall(final IdentNode identNode, final int flags) {
final Symbol symbol = identNode.getSymbol();
int scopeCallFlags = flags;
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
method.load(getScopeProtoDepth(currentBlock, symbol));
scopeCallFlags |= CALLSITE_FAST_SCOPE;
@@ -591,7 +629,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
// We don't need ScriptFunction object for 'eval'
method.pop();
- method.loadScope(); // Load up self (scope).
+ method.loadCompilerConstant(SCOPE); // Load up self (scope).
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
// load evaluated code
@@ -618,7 +656,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
if (symbol.isScope()) {
@@ -637,16 +675,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
} else {
sharedScopeCall(node, flags);
}
- assert method.peekType().equals(callNode.getType());
+ assert method.peekType().equals(callNode.getType()) : method.peekType() + "!=" + callNode.getType();
} else {
enterDefault(node);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -655,35 +693,34 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(final FunctionNode callee) {
+ public boolean enterFunctionNode(final FunctionNode origCallee) {
+ // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
+ // callee.needsCallee() == true
+ final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
+
final boolean isVarArg = callee.isVarArg();
final int argCount = isVarArg ? -1 : callee.getParameters().size();
final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
- if (callee.needsCallee()) {
- newFunctionObject(callee);
- }
-
- if (callee.isStrictMode()) { // self is undefined
+ if (callee.isStrict()) { // self is undefined
method.loadUndefined(Type.OBJECT);
} else { // get global from scope (which is the self)
globalInstance();
}
loadArgs(args, signature, isVarArg, argCount);
+ assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
- functionNodeIsCallee = true;
- callee.accept(CodeGenerator.this);
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -697,11 +734,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
// Load up function.
load(function);
method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
@@ -709,58 +746,41 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
});
method.store(callNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- if (continueNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel());
- method.splitAwareGoto(continueNode.getTargetLabel());
-
- return null;
+ return false;
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
- }
-
- @Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
- if (executeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
final Node expression = executeNode.getExpression();
expression.accept(this);
- return null;
+ return false;
}
@Override
- public Node enterForNode(final ForNode forNode) {
- if (forNode.testResolved()) {
- return null;
- }
-
+ public boolean enterForNode(final ForNode forNode) {
final Node test = forNode.getTest();
final Block body = forNode.getBody();
final Node modify = forNode.getModify();
@@ -789,6 +809,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(loopLabel);
new Store(init) {
+ @Override
+ protected void storeNonDiscard() {
+ return;
+ }
@Override
protected void evaluate() {
method.load(iter);
@@ -829,7 +853,19 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
+ }
+
+ private static int assignSlots(final Block block, final int firstSlot) {
+ int nextSlot = firstSlot;
+ for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol next = iter.next();
+ if (next.hasSlot()) {
+ next.setSlot(nextSlot);
+ nextSlot += next.slotCount();
+ }
+ }
+ return nextSlot;
}
/**
@@ -838,21 +874,26 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block with local vars.
*/
private void initLocals(final Block block) {
- final FunctionNode function = lexicalContext.getFunction(block);
- final boolean isFunctionNode = block == function;
+ final boolean isFunctionBody = getLexicalContext().isFunctionBody();
- /*
- * Get the symbols from the frame and realign the frame so that all
- * slots get correct numbers. The slot numbering is not fixed until
- * after initLocals has been run
- */
- final Frame frame = block.getFrame();
- final List symbols = frame.getSymbols();
+ final int nextFreeSlot;
+ if (isFunctionBody) {
+ // On entry to function, start with slot 0
+ nextFreeSlot = 0;
+ } else {
+ // Otherwise, continue from previous block's first free slot
+ nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ }
+ if(nextFreeSlotsSize == nextFreeSlots.length) {
+ final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
+ System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
+ nextFreeSlots = newNextFreeSlots;
+ }
+ nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
- /* Fix the predefined slots so they have numbers >= 0, like varargs. */
- frame.realign();
-
- if (isFunctionNode) {
+ final FunctionNode function = getLexicalContext().getCurrentFunction();
+ if (isFunctionBody) {
+ /* Fix the predefined slots so they have numbers >= 0, like varargs. */
if (function.needsParentScope()) {
initParentScope();
}
@@ -876,14 +917,18 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List nameList = new ArrayList<>();
final List locals = new ArrayList<>();
-
// Initalize symbols and values
final List newSymbols = new ArrayList<>();
final List values = new ArrayList<>();
final boolean hasArguments = function.needsArguments();
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+
+ final Iterator symbols = block.symbolIterator();
+
+ while (symbols.hasNext()) {
+ final Symbol symbol = symbols.next();
+
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
@@ -907,9 +952,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}
- /* Correct slot numbering again */
- frame.realign();
-
// we may have locals that need to be initialized
initSymbols(locals);
@@ -931,7 +973,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
@Override
protected void loadScope(MethodEmitter m) {
if(function.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
@@ -940,118 +982,102 @@ final class CodeGenerator extends NodeOperatorVisitor {
foc.makeObject(method);
// runScript(): merge scope into global
- if (isFunctionNode && function.isProgram()) {
+ if (isFunctionBody && function.isProgram()) {
method.invoke(ScriptRuntime.MERGE_SCOPE);
}
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
} else {
// Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
// we need to assign them separately here.
int nextParam = 0;
- if (isFunctionNode && function.isVarArg()) {
+ if (isFunctionBody && function.isVarArg()) {
for (final IdentNode param : function.getParameters()) {
param.getSymbol().setFieldIndex(nextParam++);
}
}
+
+ final Iterator iter = block.symbolIterator();
+ final List symbols = new ArrayList<>();
+ while (iter.hasNext()) {
+ symbols.add(iter.next());
+ }
initSymbols(symbols);
}
// Debugging: print symbols? @see --print-symbols flag
- printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "" : function.getIdent().getName()));
+ printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "" : function.getIdent().getName()));
}
private void initArguments(final FunctionNode function) {
- method.loadVarArgs();
+ method.loadCompilerConstant(VARARGS);
if(function.needsCallee()) {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
} else {
// If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
// caller.
- assert function.isStrictMode();
+ assert function.isStrict();
method.loadNull();
}
method.load(function.getParameters().size());
globalAllocateArguments();
- method.storeArguments();
+ method.storeCompilerConstant(ARGUMENTS);
}
private void initParentScope() {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
method.invoke(ScriptFunction.GET_SCOPE);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- final boolean isCallee = functionNodeIsCallee;
- functionNodeIsCallee = false;
-
- if (functionNode.testResolved()) {
- return null;
- }
-
- if(!(isCallee || functionNode == compiler.getFunctionNode())) {
- newFunctionObject(functionNode);
- }
-
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ // Must do it now; can't postpone it until leaveFunctionNode()
+ newFunctionObject(functionNode, functionNode);
+ return false;
}
- LOG.info("=== BEGIN " + functionNode.getName());
- lexicalContext.push(functionNode);
+ LOG.info("=== BEGIN ", functionNode.getName());
- setCurrentCompileUnit(functionNode.getCompileUnit());
- assert getCurrentCompileUnit() != null;
+ assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
+ push(functionNode.getCompileUnit());
+ assert !compileUnits.isEmpty();
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
- functionNode.setMethodEmitter(method);
+ pushMethodEmitter(unit.getClassEmitter().method(functionNode));
// Mark end for variable tables.
method.begin();
- method.label(functionNode.getEntryLabel());
- initLocals(functionNode);
- functionNode.setState(CompilationState.EMITTED);
-
- return functionNode;
+ return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- // Mark end for variable tables.
- method.label(functionNode.getBreakLabel());
-
- if (!functionNode.needsScope()) {
- method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
- }
-
- symbolInfo(functionNode);
try {
method.end(); // wrap up this method
+ pop(functionNode.getCompileUnit());
+ popMethodEmitter(method);
+ LOG.info("=== END ", functionNode.getName());
+
+ final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED);
+
+ newFunctionObject(newFunctionNode, functionNode);
+ return newFunctionNode;
} catch (final Throwable t) {
Context.printStackTrace(t);
final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
e.initCause(t);
throw e;
}
-
- lexicalContext.pop(functionNode);
- LOG.info("=== END " + functionNode.getName());
- return functionNode;
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
- return null;
+ public boolean enterIdentNode(final IdentNode identNode) {
+ return false;
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
- if (ifNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIfNode(final IfNode ifNode) {
final Node test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
@@ -1082,30 +1108,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(afterLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
- if (indexNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIndexNode(final IndexNode indexNode) {
load(indexNode);
-
- return null;
+ return false;
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- if (lineNumberNode.testResolved()) {
- return null;
- }
-
- final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getLexicalContext().getCurrentFunction().getName() + ")");
method.label(label);
method.lineNumber(lineNumberNode.getLineNumber(), label);
- return null;
+ return false;
}
/**
@@ -1131,43 +1148,43 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Type elementType = arrayType.getElementType();
if (units != null) {
- final CompileUnit savedCompileUnit = getCurrentCompileUnit();
- final MethodEmitter savedMethod = getCurrentMethodEmitter();
+ final MethodEmitter savedMethod = method;
- try {
- for (final ArrayUnit unit : units) {
- setCurrentCompileUnit(unit.getCompileUnit());
+ for (final ArrayUnit arrayUnit : units) {
+ push(arrayUnit.getCompileUnit());
- final String className = getCurrentCompileUnit().getUnitClassName();
- final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
- final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
+ final String className = unit.getUnitClassName();
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
+ final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
- method.setFunctionNode(getCurrentFunctionNode());
- method.begin();
+ final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+ pushMethodEmitter(me);
- fixScopeSlot();
+ method.setFunctionNode(getLexicalContext().getCurrentFunction());
+ method.begin();
- method.load(arrayType, SPLIT_ARRAY_ARG.slot());
+ fixScopeSlot();
- for (int i = unit.getLo(); i < unit.getHi(); i++) {
- storeElement(nodes, elementType, postsets[i]);
- }
+ method.load(arrayType, SPLIT_ARRAY_ARG.slot());
- method._return();
- method.end();
-
- savedMethod.loadThis();
- savedMethod.swap();
- savedMethod.loadCallee();
- savedMethod.swap();
- savedMethod.loadScope();
- savedMethod.swap();
- savedMethod.invokestatic(className, name, signature);
+ for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
+ storeElement(nodes, elementType, postsets[i]);
}
- } finally {
- setCurrentCompileUnit(savedCompileUnit);
- setCurrentMethodEmitter(savedMethod);
+
+ method._return();
+ method.end();
+ popMethodEmitter(me);
+
+ assert method == savedMethod;
+ method.loadCompilerConstant(THIS);
+ method.swap();
+ method.loadCompilerConstant(CALLEE);
+ method.swap();
+ method.loadCompilerConstant(SCOPE);
+ method.swap();
+ method.invokestatic(className, name, signature);
+
+ pop(unit);
}
return method;
@@ -1217,12 +1234,12 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param string string to load
*/
void loadConstant(final String string) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(string);
method.load(index);
- method.invokestatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
+ method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class));
classEmitter.needGetConstantMethod(String.class);
}
@@ -1233,14 +1250,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param object object to load
*/
void loadConstant(final Object object) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(object);
final Class> cls = object.getClass();
if (cls == PropertyMap.class) {
method.load(index);
- method.invokestatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
+ method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
classEmitter.needGetConstantMethod(PropertyMap.class);
} else if (cls.isArray()) {
method.load(index);
@@ -1303,14 +1320,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
return loadRegexToken(regexToken);
}
// emit field
- final String regexName = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
+ final ClassEmitter classEmitter = unit.getClassEmitter();
classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
regexFieldCount++;
// get field, if null create new regex, finally clone regex object
- method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.dup();
final Label cachedLabel = new Label("cached");
method.ifnonnull(cachedLabel);
@@ -1318,7 +1335,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.pop();
loadRegexToken(regexToken);
method.dup();
- method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.label(cachedLabel);
globalRegExpCopy();
@@ -1328,18 +1345,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
load(literalNode).store(literalNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterObjectNode(final ObjectNode objectNode) {
- if (objectNode.testResolved()) {
- return null;
- }
-
+ public boolean enterObjectNode(final ObjectNode objectNode) {
final List elements = objectNode.getElements();
final int size = elements.size();
@@ -1404,14 +1417,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (!hasGettersSetters) {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
for (final Node element : elements) {
final PropertyNode propertyNode = (PropertyNode)element;
final Object key = propertyNode.getKey();
- final FunctionNode getter = (FunctionNode)propertyNode.getGetter();
- final FunctionNode setter = (FunctionNode)propertyNode.getSetter();
+ final FunctionNode getter = propertyNode.getGetter();
+ final FunctionNode setter = propertyNode.getSetter();
if (getter == null && setter == null) {
continue;
@@ -1436,35 +1449,25 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- if (returnNode.testResolved()) {
- return null;
- }
+ public boolean enterReturnNode(final ReturnNode returnNode) {
+ method.registerReturn();
- // Set the split return flag in the scope if this is a split method fragment.
- if (method.getSplitNode() != null) {
- assert method.getSplitNode().hasReturn() : "unexpected return in split node";
-
- method.loadScope();
- method.checkcast(Scope.class);
- method.load(0);
- method.invoke(Scope.SET_SPLIT_STATE);
- }
+ final Type returnType = getLexicalContext().getCurrentFunction().getReturnType();
final Node expression = returnNode.getExpression();
if (expression != null) {
load(expression);
} else {
- method.loadUndefined(getCurrentFunctionNode().getReturnType());
+ method.loadUndefined(returnType);
}
- method._return(getCurrentFunctionNode().getReturnType());
+ method._return(returnType);
- return null;
+ return false;
}
private static boolean isNullLiteral(final Node node) {
@@ -1542,19 +1545,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
assert args.size() == 2;
- final Node lhs = args.get(0);
- final Node rhs = args.get(1);
-
final Type returnType = node.getType();
- load(lhs);
- load(rhs);
+
+ load(args.get(0));
+ load(args.get(1));
Request finalRequest = request;
+ //if the request is a comparison, i.e. one that can be reversed
+ //it keeps its semantic, but make sure that the object comes in
+ //last
final Request reverse = Request.reverse(request);
- if (method.peekType().isObject() && reverse != null) {
- if (!method.peekType(1).isObject()) {
- method.swap();
+ if (method.peekType().isObject() && reverse != null) { //rhs is object
+ if (!method.peekType(1).isObject()) { //lhs is not object
+ method.swap(); //prefer object as lhs
finalRequest = reverse;
}
}
@@ -1581,11 +1585,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
- if (runtimeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
/*
* First check if this should be something other than a runtime node
* AccessSpecializer might have changed the type
@@ -1624,7 +1624,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.add();
method.convert(type);
method.store(symbol);
- return null;
+ return false;
default:
// it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
// assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
@@ -1636,11 +1636,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List args = runtimeNode.getArgs();
if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
- return null;
+ return false;
}
if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
- return null;
+ return false;
}
for (final Node arg : runtimeNode.getArgs()) {
@@ -1658,129 +1658,146 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.convert(runtimeNode.getType());
method.store(runtimeNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
- if (splitNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSplitNode(final SplitNode splitNode) {
final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
- final FunctionNode fn = getCurrentFunctionNode();
+ final FunctionNode fn = getLexicalContext().getCurrentFunction();
final String className = splitCompileUnit.getUnitClassName();
final String name = splitNode.getName();
- final Class> rtype = fn.getReturnType().getTypeClass();
- final boolean needsArguments = fn.needsArguments();
- final Class>[] ptypes = needsArguments ?
+ final Class> rtype = fn.getReturnType().getTypeClass();
+ final boolean needsArguments = fn.needsArguments();
+ final Class>[] ptypes = needsArguments ?
new Class>[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} :
new Class>[] {ScriptFunction.class, Object.class, ScriptObject.class};
- setCurrentCompileUnit(splitCompileUnit);
- splitNode.setCompileUnit(splitCompileUnit);
+ final MethodEmitter caller = method;
+ push(splitCompileUnit);
final Call splitCall = staticCallNoLookup(
className,
name,
methodDescriptor(rtype, ptypes));
- setCurrentMethodEmitter(
- splitCompileUnit.getClassEmitter().method(
- EnumSet.of(Flag.PUBLIC, Flag.STATIC),
- name,
- rtype,
- ptypes));
+ final MethodEmitter splitEmitter =
+ splitCompileUnit.getClassEmitter().method(
+ splitNode,
+ name,
+ rtype,
+ ptypes);
+
+ pushMethodEmitter(splitEmitter);
method.setFunctionNode(fn);
- method.setSplitNode(splitNode);
- splitNode.setMethodEmitter(method);
- final MethodEmitter caller = splitNode.getCaller();
- if(fn.needsCallee()) {
- caller.loadCallee();
+ if (fn.needsCallee()) {
+ caller.loadCompilerConstant(CALLEE);
} else {
caller.loadNull();
}
- caller.loadThis();
- caller.loadScope();
+ caller.loadCompilerConstant(THIS);
+ caller.loadCompilerConstant(SCOPE);
if (needsArguments) {
- caller.loadArguments();
+ caller.loadCompilerConstant(ARGUMENTS);
}
caller.invoke(splitCall);
- caller.storeResult();
+ caller.storeCompilerConstant(RETURN);
method.begin();
method.loadUndefined(fn.getReturnType());
- method.storeResult();
+ method.storeCompilerConstant(RETURN);
fixScopeSlot();
- return splitNode;
+ return true;
}
private void fixScopeSlot() {
- if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
+ if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
// TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
}
@Override
public Node leaveSplitNode(final SplitNode splitNode) {
+ assert method instanceof SplitMethodEmitter;
+ final boolean hasReturn = method.hasReturn();
+ final List